From 84f1a15a4c1e99394e8559832ec4ff1be0285126 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 6 Dec 2022 15:06:40 +0000 Subject: [PATCH 001/144] Refactor Audit Worker to use Utopia Queue system --- app/cli.php | 5 ++ app/init.php | 4 +- app/worker.php | 11 +++++ app/workers/audits.php | 91 ++++++++++++++++++++++-------------- src/Appwrite/Event/Audit.php | 8 +++- 5 files changed, 82 insertions(+), 37 deletions(-) diff --git a/app/cli.php b/app/cli.php index 13709b9b57..8120f5ff68 100644 --- a/app/cli.php +++ b/app/cli.php @@ -4,6 +4,7 @@ require_once __DIR__ . '/init.php'; require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Func; +use Appwrite\Event\Audit; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; use Utopia\Database\Validator\Authorization; @@ -114,6 +115,10 @@ CLI::setResource('queueForFunctions', function (Group $pools) { return new Func($pools->get('queue')->pop()->getResource()); }, ['pools']); +CLI::setResource('audits', function (Group $pools) { + return new Audit($pools->get('queue')->pop()->getResource()); +}, ['pools']); + CLI::setResource('logError', function (Registry $register) { return function (Throwable $error, string $namespace, string $action) use ($register) { $logger = $register->get('logger'); diff --git a/app/init.php b/app/init.php index 31f82740b6..c6e946e2b1 100644 --- a/app/init.php +++ b/app/init.php @@ -847,7 +847,9 @@ App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en'))) // Queues App::setResource('events', fn() => new Event('', '')); -App::setResource('audits', fn() => new Audit()); +App::setResource('audits', function (Group $pools) { + return new Audit($pools->get('queue')->pop()->getResource()); +}, ['pools']); App::setResource('mails', fn() => new Mail()); App::setResource('deletes', fn() => new Delete()); App::setResource('database', fn() => new EventDatabase()); diff --git a/app/worker.php b/app/worker.php index 42a5f92439..f7644ffe98 100644 --- a/app/worker.php +++ b/app/worker.php @@ -2,6 +2,7 @@ require_once __DIR__ . '/init.php'; +use Appwrite\Event\Audit; use Appwrite\Event\Func; use Swoole\Runtime; use Utopia\App; @@ -85,6 +86,16 @@ Server::setResource('queueForFunctions', function (Registry $register) { ); }, ['register']); +Server::setResource('audits', function (Registry $register) { + $pools = $register->get('pools'); + return new Audit( + $pools + ->get('queue') + ->pop() + ->getResource() + ); +}, ['register']); + Server::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); diff --git a/app/workers/audits.php b/app/workers/audits.php index 90ac020536..9ca0a27a30 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -1,42 +1,33 @@ args['event']; - $payload = $this->args['payload']; - $mode = $this->args['mode']; - $resource = $this->args['resource']; - $userAgent = $this->args['userAgent']; - $ip = $this->args['ip']; - - $user = new Document($this->args['user']); - $project = new Document($this->args['project']); - $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); - $dbForProject = $this->getProjectDB($project); $audit = new Audit($dbForProject); $audit->log( userId: $user->getId(), @@ -53,9 +44,41 @@ class AuditsV1 extends Worker 'data' => $payload, ] ); - } + }; +}); - public function shutdown(): void - { - } -} +$server->job() + ->inject('message') + ->inject('dbForProject') + ->inject('execute') + ->action(function (Message $message, Database $dbForProject, callable $execute) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $event = $payload['event'] ?? ''; + $auditPayload = $payload['payload'] ?? ''; + $mode = $payload['mode'] ?? ''; + $resource = $payload['resource'] ?? ''; + $userAgent = $payload['userAgent'] ?? ''; + $ip = $payload['ip'] ?? ''; + $project = new Document($payload['project'] ?? []); + $user = new Document($payload['user'] ?? []); + + $execute( + $dbForProject, + $event, + $auditPayload, + $mode, + $resource, + $userAgent, + $ip, + $user, + $project + ); + }); + +$server->workerStart(); +$server->start(); \ No newline at end of file diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index 254f7c294a..9be6e27b33 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -3,6 +3,8 @@ namespace Appwrite\Event; use Resque; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Audit extends Event { @@ -11,7 +13,7 @@ class Audit extends Event protected string $userAgent = ''; protected string $ip = ''; - public function __construct() + public function __construct(protected Connection $connection) { parent::__construct(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME); } @@ -116,7 +118,9 @@ class Audit extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'payload' => $this->payload, From 85268cbae2be5ae38d171c68cce94b7659d115ca Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 6 Dec 2022 15:15:16 +0000 Subject: [PATCH 002/144] Run Linter --- app/workers/audits.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/workers/audits.php b/app/workers/audits.php index 9ca0a27a30..bfea0be217 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -23,8 +23,7 @@ Server::setResource('execute', function () { string $ip, Document $user, Document $project - ) - { + ) { $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); @@ -81,4 +80,4 @@ $server->job() }); $server->workerStart(); -$server->start(); \ No newline at end of file +$server->start(); From 21afa4312749fe4fc5ff6ff3d6460fd2c38f8a6b Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 13 Dec 2022 11:16:12 +0000 Subject: [PATCH 003/144] Port Certificates to new Queue system --- app/cli.php | 6 + app/controllers/api/projects.php | 6 +- app/controllers/general.php | 5 +- app/init.php | 4 + app/worker.php | 11 + app/workers/certificates.php | 590 ++++++++++---------- docker-compose.yml | 1 + src/Appwrite/Event/Certificate.php | 8 +- src/Appwrite/Platform/Tasks/Maintenance.php | 16 +- 9 files changed, 339 insertions(+), 308 deletions(-) diff --git a/app/cli.php b/app/cli.php index bfe7bfcefb..860a1ef946 100644 --- a/app/cli.php +++ b/app/cli.php @@ -3,6 +3,7 @@ require_once __DIR__ . '/init.php'; require_once __DIR__ . '/controllers/general.php'; +use Appwrite\Event\Certificate; use Appwrite\Event\Func; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; @@ -144,6 +145,11 @@ CLI::setResource('queueForFunctions', function (Group $pools) { return new Func($pools->get('queue')->pop()->getResource()); }, ['pools']); +CLI::setResource('queueForCertificates', function (Group $pools) { + var_dump(json_encode($pools)); + return new Certificate($pools->get('queue')->pop()->getResource()); +}, ['pools']); + CLI::setResource('logError', function (Registry $register) { return function (Throwable $error, string $namespace, string $action) use ($register) { $logger = $register->get('logger'); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 5fef7eab78..87bba655c5 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1314,7 +1314,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { + ->inject('queueForCertificates') + ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Certificate $queueForCertificates) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1352,8 +1353,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $dbForConsole->deleteCachedDocument('projects', $project->getId()); // Issue a TLS certificate when domain is verified - $event = new Certificate(); - $event + $queueForCertificates ->setDomain($domain) ->trigger(); diff --git a/app/controllers/general.php b/app/controllers/general.php index 0c10dd46e9..1e894d087a 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -51,7 +51,8 @@ App::init() ->inject('locale') ->inject('clients') ->inject('servers') - ->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients, array $servers) { + ->inject('queueForCertificates') + ->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients, array $servers, Certificate $queueForCertificates) { /* * Request format */ @@ -122,7 +123,7 @@ App::init() Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...'); - (new Certificate()) + $queueForCertificates ->setDomain($domainDocument) ->trigger(); } diff --git a/app/init.php b/app/init.php index 2f71a60ce8..b606669770 100644 --- a/app/init.php +++ b/app/init.php @@ -69,6 +69,7 @@ use Utopia\Pools\Group; use Utopia\Pools\Pool; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; +use Appwrite\Event\Certificate; use Appwrite\Event\Func; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; @@ -860,6 +861,9 @@ App::setResource('queue', function (Group $pools) { App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); +App::setResource('queueForCertificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); App::setResource('usage', function ($register) { return new Stats($register->get('statsd')); }, ['register']); diff --git a/app/worker.php b/app/worker.php index 8151381d4a..cf6fa2e342 100644 --- a/app/worker.php +++ b/app/worker.php @@ -2,6 +2,7 @@ require_once __DIR__ . '/init.php'; +use Appwrite\Event\Certificate; use Appwrite\Event\Func; use Swoole\Runtime; use Utopia\App; @@ -85,6 +86,16 @@ Server::setResource('queueForFunctions', function (Registry $register) { ); }, ['register']); +Server::setResource('queueForCertificates', function (Registry $register) { + $pools = $register->get('pools'); + return new Certificate( + $pools + ->get('queue') + ->pop() + ->getResource() + ); +}, ['register']); + Server::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); diff --git a/app/workers/certificates.php b/app/workers/certificates.php index b4f0701c46..b5f6c52685 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -1,42 +1,33 @@ dbForConsole = $this->getConsoleDB(); - - $skipCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file - $document = new Document($this->args['domain'] ?? []); - $domain = new Domain($document->getAttribute('domain', '')); - // Get current certificate - $certificate = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]); + $certificate = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]); // If we don't have certificate for domain yet, let's create new document. At the end we save it if (!$certificate) { @@ -90,14 +75,14 @@ class CertificatesV1 extends Worker } // Validate domain and DNS records. Skip if job is forced - if (!$skipCheck) { - $mainDomain = $this->getMainDomain(); + if (!$skipRenewCheck) { + $mainDomain = getMainDomain($dbForConsole); $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; - $this->validateDomain($domain, $isMainDomain); + validateDomain($domain, $isMainDomain); } // If certificate exists already, double-check expiry date. Skip if job is forced - if (!$skipCheck && !$this->isRenewRequired($domain->get())) { + if (!$skipRenewCheck && !isRenewRequired($domain->get())) { throw new Exception('Renew isn\'t required.'); } @@ -105,7 +90,7 @@ class CertificatesV1 extends Worker $folder = ID::unique(); // Generate certificate files using Let's Encrypt - $letsEncryptData = $this->issueCertificate($folder, $domain->get(), $email); + $letsEncryptData = issueCertificate($folder, $domain->get(), $email); // Command succeeded, store all data into document // We store stderr too, because it may include warnings @@ -115,10 +100,10 @@ class CertificatesV1 extends Worker ])); // Give certificates to Traefik - $this->applyCertificateFiles($folder, $domain->get(), $letsEncryptData); + applyCertificateFiles($folder, $domain->get(), $letsEncryptData); // Update certificate info stored in database - $certificate->setAttribute('renewDate', $this->getRenewDate($domain->get())); + $certificate->setAttribute('renewDate', getRenewDate($domain->get())); $certificate->setAttribute('attempts', 0); $certificate->setAttribute('issueDate', DateTime::now()); } catch (Throwable $e) { @@ -133,290 +118,309 @@ class CertificatesV1 extends Worker $certificate->setAttribute('renewDate', DateTime::now()); // Send email to security email - $this->notifyError($domain->get(), $e->getMessage(), $attempts); + notifyError($domain->get(), $e->getMessage(), $attempts); } finally { // All actions result in new updatedAt date $certificate->setAttribute('updated', DateTime::now()); // Save all changes we made to certificate document into database - $this->saveCertificateDocument($domain->get(), $certificate); + saveCertificateDocument($domain->get(), $certificate, $dbForConsole); + } + }; +}); + + +/** + * Save certificate data into database. + * + * @param string $domain Domain name that certificate is for + * @param Document $certificate Certificate document that we need to save + * @param Database $dbForConsole Database connection for console + * + * @return void + */ +function saveCertificateDocument(string $domain, Document $certificate, Database $dbForConsole) : void +{ + // Check if update or insert required + $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); + if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { + // Merge new data with current data + $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + + $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + } else { + $certificate = $dbForConsole->createDocument('certificates', $certificate); + } + + $certificateId = $certificate->getId(); + updateDomainDocuments($certificateId, $domain, $dbForConsole); +} + +/** + * Get main domain. Needed as we do different checks for main and non-main domains. + * + * @return null|string Returns main domain. If null, there is no main domain yet. + */ +function getMainDomain($dbForConsole): ?string +{ + $envDomain = App::getEnv('_APP_DOMAIN', ''); + if (!empty($envDomain) && $envDomain !== 'localhost') { + return $envDomain; + } else { + $domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]); + if ($domainDocument) { + return $domainDocument->getAttribute('domain'); } } - public function shutdown(): void - { + return null; +} + +/** + * Internal domain validation functionality to prevent unnecessary attempts failed from Let's Encrypt side. We check: + * - Domain needs to be public and valid (prevents NFT domains that are not supported by Let's Encrypt) + * - Domain must have proper DNS record + * + * @param Domain $domain Domain which we validate + * @param bool $isMainDomain In case of master domain, we look for different DNS configurations + * + * @return void + */ +function validateDomain(Domain $domain, bool $isMainDomain): void +{ + if (empty($domain->get())) { + throw new Exception('Missing certificate domain.'); } - /** - * Save certificate data into database. - * - * @param string $domain Domain name that certificate is for - * @param Document $certificate Certificate document that we need to save - * - * @return void - */ - private function saveCertificateDocument(string $domain, Document $certificate): void - { - // Check if update or insert required - $certificateDocument = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); - if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { - // Merge new data with current data - $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); - - $certificate = $this->dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); - } else { - $certificate = $this->dbForConsole->createDocument('certificates', $certificate); - } - - $certificateId = $certificate->getId(); - $this->updateDomainDocuments($certificateId, $domain); + if (!$domain->isKnown() || $domain->isTest()) { + throw new Exception('Unknown public suffix for domain.'); } - /** - * Get main domain. Needed as we do different checks for main and non-main domains. - * - * @return null|string Returns main domain. If null, there is no main domain yet. - */ - private function getMainDomain(): ?string - { - $envDomain = App::getEnv('_APP_DOMAIN', ''); - if (!empty($envDomain) && $envDomain !== 'localhost') { - return $envDomain; - } else { - $domainDocument = $this->dbForConsole->findOne('domains', [Query::orderAsc('_id')]); - if ($domainDocument) { - return $domainDocument->getAttribute('domain'); - } + if (!$isMainDomain) { + // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? + // Validate if domain target is properly configured + $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + + if (!$target->isKnown() || $target->isTest()) { + throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); } - return null; + // Verify domain with DNS records + $validator = new CNAME($target->get()); + if (!$validator->isValid($domain->get())) { + throw new Exception('Failed to verify domain DNS records.'); + } + } else { + // Main domain validation + // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? } +} - /** - * Internal domain validation functionality to prevent unnecessary attempts failed from Let's Encrypt side. We check: - * - Domain needs to be public and valid (prevents NFT domains that are not supported by Let's Encrypt) - * - Domain must have proper DNS record - * - * @param Domain $domain Domain which we validate - * @param bool $isMainDomain In case of master domain, we look for different DNS configurations - * - * @return void - */ - private function validateDomain(Domain $domain, bool $isMainDomain): void - { - if (empty($domain->get())) { - throw new Exception('Missing certificate domain.'); - } +/** + * Reads expiry date of certificate from file and decides if renewal is required or not. + * + * @param string $domain Domain for which we check certificate file + * + * @return bool True, if certificate needs to be renewed + */ +function isRenewRequired(string $domain): bool +{ + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + if (\file_exists($certPath)) { + $validTo = null; - if (!$domain->isKnown() || $domain->isTest()) { - throw new Exception('Unknown public suffix for domain.'); - } - - if (!$isMainDomain) { - // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? - - // Validate if domain target is properly configured - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); - - if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); - } - - // Verify domain with DNS records - $validator = new CNAME($target->get()); - if (!$validator->isValid($domain->get())) { - throw new Exception('Failed to verify domain DNS records.'); - } - } else { - // Main domain validation - // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? - } - } - - /** - * Reads expiry date of certificate from file and decides if renewal is required or not. - * - * @param string $domain Domain for which we check certificate file - * - * @return bool True, if certificate needs to be renewed - */ - private function isRenewRequired(string $domain): bool - { - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; - if (\file_exists($certPath)) { - $validTo = null; - - $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? 0; - - if (empty($validTo)) { - throw new Exception('Unable to read certificate file (cert.pem).'); - } - - // LetsEncrypt allows renewal 30 days before expiry - $expiryInAdvance = (60 * 60 * 24 * 30); - if ($validTo - $expiryInAdvance > \time()) { - return false; - } - } - - return true; - } - - /** - * LetsEncrypt communication to issue certificate (using certbot CLI) - * - * @param string $folder Folder into which certificates should be generated - * @param string $domain Domain to generate certificate for - * - * @return array Named array with keys 'stdout' and 'stderr', both string - */ - private function issueCertificate(string $folder, string $domain, string $email): array - { - $stdout = ''; - $stderr = ''; - - $staging = (App::isProduction()) ? '' : ' --dry-run'; - $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" - . " --email " . $email - . " --cert-name " . $folder - . " -w " . APP_STORAGE_CERTIFICATES - . " -d {$domain}", '', $stdout, $stderr); - - // Unexpected error, usually 5XX, API limits, ... - if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $stderr); - } - - return [ - 'stdout' => $stdout, - 'stderr' => $stderr - ]; - } - - /** - * Read new renew date from certificate file generated by Let's Encrypt - * - * @param string $domain Domain which certificate was generated for - * - * @return string - */ - private function getRenewDate(string $domain): string - { - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? null; - $dt = (new \DateTime())->setTimestamp($validTo); - return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days - } + $validTo = $certData['validTo_time_t'] ?? 0; - /** - * Method to take files from Let's Encrypt, and put it into Traefik. - * - * @param string $domain Domain which certificate was generated for - * @param string $folder Folder in which certificates were generated - * @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error - * - * @return void - */ - private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void - { - // Prepare folder in storage for domain - $path = APP_STORAGE_CERTIFICATES . '/' . $domain; - if (!\is_readable($path)) { - if (!\mkdir($path, 0755, true)) { - throw new Exception('Failed to create path for certificate.'); - } + if (empty($validTo)) { + throw new Exception('Unable to read certificate file (cert.pem).'); } - // Move generated files - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - $config = \implode(PHP_EOL, [ - "tls:", - " certificates:", - " - certFile: /storage/certificates/{$domain}/fullchain.pem", - " keyFile: /storage/certificates/{$domain}/privkey.pem" - ]); - - // Save configuration into Traefik using our new cert files - if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { - throw new Exception('Failed to save Traefik configuration.'); + // LetsEncrypt allows renewal 30 days before expiry + $expiryInAdvance = (60 * 60 * 24 * 30); + if ($validTo - $expiryInAdvance > \time()) { + return false; } } - /** - * Method to make sure information about error is delivered to admnistrator. - * - * @param string $domain Domain that caused the error - * @param string $errorMessage Verbose error message - * @param int $attempt How many times it failed already - * - * @return void - */ - private function notifyError(string $domain, string $errorMessage, int $attempt): void - { - // Log error into console - Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); + return true; +} - // Send mail to administratore mail - $mail = new Mail(); - $mail - ->setType(MAIL_TYPE_CERTIFICATE) - ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) - ->setUrl('https://' . $domain) - ->setLocale(App::getEnv('_APP_LOCALE', 'en')) - ->setName('Appwrite Administrator') - ->setPayload([ - 'domain' => $domain, - 'error' => $errorMessage, - 'attempt' => $attempt - ]) - ->trigger(); +/** + * LetsEncrypt communication to issue certificate (using certbot CLI) + * + * @param string $folder Folder into which certificates should be generated + * @param string $domain Domain to generate certificate for + * + * @return array Named array with keys 'stdout' and 'stderr', both string + */ +function issueCertificate(string $folder, string $domain, string $email): array +{ + $stdout = ''; + $stderr = ''; + + $staging = (App::isProduction()) ? '' : ' --dry-run'; + $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + . " --email " . $email + . " --cert-name " . $folder + . " -w " . APP_STORAGE_CERTIFICATES + . " -d {$domain}", '', $stdout, $stderr); + + // Unexpected error, usually 5XX, API limits, ... + if ($exit !== 0) { + throw new Exception('Failed to issue a certificate with message: ' . $stderr); } - /** - * Update all existing domain documents so they have relation to correct certificate document. - * This solved issues: - * - when adding a domain for which there is already a certificate - * - when renew creates new document? It might? - * - overall makes it more reliable - * - * @param string $certificateId ID of a new or updated certificate document - * @param string $domain Domain that is affected by new certificate - * - * @return void - */ - private function updateDomainDocuments(string $certificateId, string $domain): void - { - $domains = $this->dbForConsole->find('domains', [ - Query::equal('domain', [$domain]), - Query::limit(1000), - ]); + return [ + 'stdout' => $stdout, + 'stderr' => $stderr + ]; +} - foreach ($domains as $domainDocument) { - $domainDocument->setAttribute('updated', DateTime::now()); - $domainDocument->setAttribute('certificateId', $certificateId); +/** + * Read new renew date from certificate file generated by Let's Encrypt + * + * @param string $domain Domain which certificate was generated for + * + * @return string + */ +function getRenewDate(string $domain): string +{ + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? null; + $dt = (new \DateTime())->setTimestamp($validTo); + return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days +} - $this->dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); +/** + * Method to take files from Let's Encrypt, and put it into Traefik. + * + * @param string $domain Domain which certificate was generated for + * @param string $folder Folder in which certificates were generated + * @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error + * + * @return void + */ +function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void +{ + // Prepare folder in storage for domain + $path = APP_STORAGE_CERTIFICATES . '/' . $domain; + if (!\is_readable($path)) { + if (!\mkdir($path, 0755, true)) { + throw new Exception('Failed to create path for certificate.'); + } + } - if ($domainDocument->getAttribute('projectId')) { - $this->dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); - } + // Move generated files + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + $config = \implode(PHP_EOL, [ + "tls:", + " certificates:", + " - certFile: /storage/certificates/{$domain}/fullchain.pem", + " keyFile: /storage/certificates/{$domain}/privkey.pem" + ]); + + // Save configuration into Traefik using our new cert files + if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { + throw new Exception('Failed to save Traefik configuration.'); + } +} + +/** + * Method to make sure information about error is delivered to admnistrator. + * + * @param string $domain Domain that caused the error + * @param string $errorMessage Verbose error message + * @param int $attempt How many times it failed already + * + * @return void + */ +function notifyError(string $domain, string $errorMessage, int $attempt): void +{ + // Log error into console + Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); + + // Send mail to administratore mail + $mail = new Mail(); + $mail + ->setType(MAIL_TYPE_CERTIFICATE) + ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) + ->setUrl('https://' . $domain) + ->setLocale(App::getEnv('_APP_LOCALE', 'en')) + ->setName('Appwrite Administrator') + ->setPayload([ + 'domain' => $domain, + 'error' => $errorMessage, + 'attempt' => $attempt + ]) + ->trigger(); +} + +/** + * Update all existing domain documents so they have relation to correct certificate document. + * This solved issues: + * - when adding a domain for which there is already a certificate + * - when renew creates new document? It might? + * - overall makes it more reliable + * + * @param string $certificateId ID of a new or updated certificate document + * @param string $domain Domain that is affected by new certificate + * @param Database $dbForConsole Database instance for console + * + * @return void + */ +function updateDomainDocuments(string $certificateId, string $domain, Database $dbForConsole): void +{ + $domains = $dbForConsole->find('domains', [ + Query::equal('domain', [$domain]), + Query::limit(1000), + ]); + + foreach ($domains as $domainDocument) { + $domainDocument->setAttribute('updated', DateTime::now()); + $domainDocument->setAttribute('certificateId', $certificateId); + + $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); + + if ($domainDocument->getAttribute('projectId')) { + $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); } } } + +$server->job() + ->inject('message') + ->inject('dbForConsole') + ->inject('execute') + ->action(function ($message, $dbForConsole, $execute) use ($server) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $document = new Document($payload['domain'] ?? []); + $domain = new Domain($document->getAttribute('domain', '')); + $skipRenewCheck = $payload['skipRenewCheck'] ?? false; + + $execute($dbForConsole, $document, $domain, $skipRenewCheck); + }); + +$server->workerStart(); +$server->start(); diff --git a/docker-compose.yml b/docker-compose.yml index 6c040df4cc..0e002e8de7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -589,6 +589,7 @@ services: - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_CONNECTIONS_QUEUE appwrite-usage: entrypoint: usage diff --git a/src/Appwrite/Event/Certificate.php b/src/Appwrite/Event/Certificate.php index d3d9091804..0f0cbaa73d 100644 --- a/src/Appwrite/Event/Certificate.php +++ b/src/Appwrite/Event/Certificate.php @@ -4,13 +4,15 @@ namespace Appwrite\Event; use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Certificate extends Event { protected bool $skipRenewCheck = false; protected ?Document $domain = null; - public function __construct() + public function __construct(protected Connection $connection) { parent::__construct(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME); } @@ -69,7 +71,9 @@ class Certificate extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + return $client->enqueue([ 'project' => $this->project, 'domain' => $this->domain, 'skipRenewCheck' => $this->skipRenewCheck diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 0739923e34..87995b0e1e 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -25,10 +25,11 @@ class Maintenance extends Action $this ->desc('Schedules maintenance tasks and publishes them to resque') ->inject('dbForConsole') - ->callback(fn (Database $dbForConsole) => $this->action($dbForConsole)); + ->inject('queueForCertificates') + ->callback(fn (Database $dbForConsole, Certificate $queueForCertificates) => $this->action($dbForConsole, $queueForCertificates)); } - public function action(Database $dbForConsole): void + public function action(Database $dbForConsole, Certificate $queueForCertificates): void { Console::title('Maintenance V1'); Console::success(APP_NAME . ' maintenance process v1 has started'); @@ -80,7 +81,7 @@ class Maintenance extends Action ->trigger(); } - function renewCertificates($dbForConsole) + function renewCertificates($dbForConsole, $queueForCertificates) { $time = DateTime::now(); @@ -91,12 +92,11 @@ class Maintenance extends Action ]); - if (\count($certificates) > 0) { + if (\count($certificates) > 0 || true) { Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); - $event = new Certificate(); foreach ($certificates as $certificate) { - $event + $queueForCertificates ->setDomain(new Document([ 'domain' => $certificate->getAttribute('domain') ])) @@ -135,7 +135,7 @@ class Maintenance extends Action $cacheRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days $schedulesDeletionRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day - Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole) { + Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole, $queueForCertificates) { $time = DateTime::now(); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); @@ -145,7 +145,7 @@ class Maintenance extends Action notifyDeleteUsageStats($usageStatsRetentionHourly); notifyDeleteConnections(); notifyDeleteExpiredSessions(); - renewCertificates($dbForConsole); + renewCertificates($dbForConsole, $queueForCertificates); notifyDeleteCache($cacheRetention); notifyDeleteSchedules($schedulesDeletionRetention); }, $interval); From b7d7a71994237aee8504b71929b8b61d5a73d30f Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 13 Dec 2022 11:18:38 +0000 Subject: [PATCH 004/144] Run Linter --- app/workers/certificates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index b5f6c52685..4d9b4a4380 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -139,7 +139,7 @@ Server::setResource('execute', function () { * * @return void */ -function saveCertificateDocument(string $domain, Document $certificate, Database $dbForConsole) : void +function saveCertificateDocument(string $domain, Document $certificate, Database $dbForConsole): void { // Check if update or insert required $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); From 6e6a6d551843d6e33bb4eaec092cfa30e5547c24 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 13 Dec 2022 15:54:01 +0000 Subject: [PATCH 005/144] Migrate Database worker to Utopia Queue system --- app/cli.php | 5 + app/init.php | 4 +- app/worker.php | 11 + app/workers/databases.php | 690 +++++++++++++++++--------------- src/Appwrite/Event/Database.php | 8 +- 5 files changed, 398 insertions(+), 320 deletions(-) diff --git a/app/cli.php b/app/cli.php index 13709b9b57..d0e3c951b8 100644 --- a/app/cli.php +++ b/app/cli.php @@ -3,6 +3,7 @@ require_once __DIR__ . '/init.php'; require_once __DIR__ . '/controllers/general.php'; +use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Func; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; @@ -114,6 +115,10 @@ CLI::setResource('queueForFunctions', function (Group $pools) { return new Func($pools->get('queue')->pop()->getResource()); }, ['pools']); +CLI::setResource('database', function (Group $pools) { + return new EventDatabase($pools->get('queue')->pop()->getResource()); +}, ['pools']); + CLI::setResource('logError', function (Registry $register) { return function (Throwable $error, string $namespace, string $action) use ($register) { $logger = $register->get('logger'); diff --git a/app/init.php b/app/init.php index 31f82740b6..7463cf3b04 100644 --- a/app/init.php +++ b/app/init.php @@ -850,7 +850,9 @@ App::setResource('events', fn() => new Event('', '')); App::setResource('audits', fn() => new Audit()); App::setResource('mails', fn() => new Mail()); App::setResource('deletes', fn() => new Delete()); -App::setResource('database', fn() => new EventDatabase()); +App::setResource('database', function (Group $pools) { + return new EventDatabase($pools->get('queue')->pop()->getResource()); +}, ['pools']); App::setResource('messaging', fn() => new Phone()); App::setResource('queueForFunctions', function (Group $pools) { return new Func($pools->get('queue')->pop()->getResource()); diff --git a/app/worker.php b/app/worker.php index 42a5f92439..259a2d8c69 100644 --- a/app/worker.php +++ b/app/worker.php @@ -2,6 +2,7 @@ require_once __DIR__ . '/init.php'; +use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Func; use Swoole\Runtime; use Utopia\App; @@ -75,6 +76,16 @@ Server::setResource('cache', function (Registry $register) { return new Cache(new Sharding($adapters)); }, ['register']); +Server::setResource('database', function (Registry $register) { + $pools = $register->get('pools'); + return new EventDatabase( + $pools + ->get('queue') + ->pop() + ->getResource() + ); +}, ['register']); + Server::setResource('queueForFunctions', function (Registry $register) { $pools = $register->get('pools'); return new Func( diff --git a/app/workers/databases.php b/app/workers/databases.php index fece5bc600..e5686203e1 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -2,28 +2,380 @@ use Appwrite\Event\Event; use Appwrite\Messaging\Adapter\Realtime; -use Appwrite\Resque\Worker; +use Utopia\App; +use Utopia\Cache\Cache; use Utopia\CLI\Console; +use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; +use Utopia\Queue\Message; +use Utopia\Cache\Adapter\Redis as RedisCache; +use Utopia\Cache\Adapter\Sharding; +use Utopia\Config\Config; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Queue\Server; -require_once __DIR__ . '/../init.php'; +use function Swoole\Coroutine\Http\get; -Console::title('Database V1 Worker'); -Console::success(APP_NAME . ' database worker v1 has started' . "\n"); +require_once __DIR__ . '/../worker.php'; -class DatabaseV1 extends Worker +Authorization::disable(); +Authorization::setDefaultStatus(false); + +const DATABASE_PROJECT = 'project'; +const DATABASE_CONSOLE = 'console'; + +function getCache(): Cache { - public function init(): void - { + global $register; + + $pools = $register->get('pools'); + /** @var \Utopia\Pools\Group $pools */ + + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource(); } - public function run(): void - { - $type = $this->args['type']; - $project = new Document($this->args['project']); - $collection = new Document($this->args['collection'] ?? []); - $document = new Document($this->args['document'] ?? []); - $database = new Document($this->args['database'] ?? []); + return new Cache(new Sharding($adapters)); +} + +/** + * Get Project DB + * + * @param Document $project + * @returns Database + */ +function getProjectDB(Document $project): Database +{ + global $register; + + /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); + + if ($project->isEmpty() || $project->getId() === 'console') { + return getConsoleDB(); + } + + $dbAdapter = $pools + ->get($project->getAttribute('database')) + ->pop() + ->getResource() + ; + + $database = new Database($dbAdapter, getCache()); + $database->setNamespace('_' . $project->getInternalId()); + + return $database; +} + + +/** + * @param Document $database + * @param Document $collection + * @param Document $attribute + * @param Database $dbForConsole + * + * @param Document $project + */ +function createDBAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole): void +{ + $dbForProject = getProjectDB($project); + + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'attributeId' => $attribute->getId() + ]); + /** + * Fetch attribute from the database, since with Resque float values are loosing informations. + */ + $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); + + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $type = $attribute->getAttribute('type', ''); + $size = $attribute->getAttribute('size', 0); + $required = $attribute->getAttribute('required', false); + $default = $attribute->getAttribute('default', null); + $signed = $attribute->getAttribute('signed', true); + $array = $attribute->getAttribute('array', false); + $format = $attribute->getAttribute('format', ''); + $formatOptions = $attribute->getAttribute('formatOptions', []); + $filters = $attribute->getAttribute('filters', []); + + try { + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create Attribute'); + } + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $attribute, + project: $project, + ); + + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); +} + +/** + * @param Document $database + * @param Document $collection + * @param Document $attribute + * @param Database $dbForConsole + * @param Document $project + */ +function deleteDBAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole): void +{ + $dbForProject = getProjectDB($project); + + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'attributeId' => $attribute->getId() + ]); + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $status = $attribute->getAttribute('status', ''); + + // possible states at this point: + // - available: should not land in queue; controller flips these to 'deleting' + // - processing: hasn't finished creating + // - deleting: was available, in deletion queue for first time + // - failed: attribute was never created + // - stuck: attribute was available but cannot be removed + try { + if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new Exception('Failed to delete Attribute'); + } + $dbForProject->deleteDocument('attributes', $attribute->getId()); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $attribute, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + // The underlying database removes/rebuilds indexes when attribute is removed + // Update indexes table with changes + /** @var Document[] $indexes */ + $indexes = $collection->getAttribute('indexes', []); + + foreach ($indexes as $index) { + /** @var string[] $attributes */ + $attributes = $index->getAttribute('attributes'); + $lengths = $index->getAttribute('lengths'); + $orders = $index->getAttribute('orders'); + + $found = \array_search($key, $attributes); + + if ($found !== false) { + // If found, remove entry from attributes, lengths, and orders + // array_values wraps array_diff to reindex array keys + // when found attribute is removed from array + $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); + $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); + $orders = \array_values(\array_diff($orders, [$orders[$found]])); + + if (empty($attributes)) { + $dbForProject->deleteDocument('indexes', $index->getId()); + } else { + $index + ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) + ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) + ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); + + // Check if an index exists with the same attributes and orders + $exists = false; + foreach ($indexes as $existing) { + if ( + $existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself + && $existing->getAttribute('attributes') === $index->getAttribute('attributes') + && $existing->getAttribute('orders') === $index->getAttribute('orders') + ) { + $exists = true; + break; + } + } + + if ($exists) { // Delete the duplicate if created, else update in db + deleteIndex($database, $collection, $index, $project, $dbForConsole); + } else { + $dbForProject->updateDocument('indexes', $index->getId(), $index); + } + } + } + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); +} + +/** + * @param Document $database + * @param Document $collection + * @param Document $index + * @param Database $dbForConsole + * @param Document $project + */ +function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole): void +{ + $dbForProject = getProjectDB($project); + + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'indexId' => $index->getId() + ]); + $collectionId = $collection->getId(); + $key = $index->getAttribute('key', ''); + $type = $index->getAttribute('type', ''); + $attributes = $index->getAttribute('attributes', []); + $lengths = $index->getAttribute('lengths', []); + $orders = $index->getAttribute('orders', []); + + try { + if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + throw new Exception('Failed to create Index'); + } + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $index, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $index->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); +} + +/** + * @param Document $database + * @param Document $collection + * @param Document $index + * @param Database $dbForConsole + * @param Document $project + */ +function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole): void +{ + $dbForProject = getProjectDB($project); + + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'indexId' => $index->getId() + ]); + $key = $index->getAttribute('key'); + $status = $index->getAttribute('status', ''); + + try { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new Exception('Failed to delete index'); + } + $dbForProject->deleteDocument('indexes', $index->getId()); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $index, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $index->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); +} + +$server->job() + ->inject('message') + ->inject('dbForProject') + ->action(function (Message $message, Database $dbForProject) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $type = $payload['type']; + $project = new Document($payload['project']); + $collection = new Document($payload['collection'] ?? []); + $document = new Document($payload['document'] ?? []); + $database = new Document($payload['database'] ?? []); if ($collection->isEmpty()) { throw new Exception('Missing collection'); @@ -35,319 +387,23 @@ class DatabaseV1 extends Worker switch (strval($type)) { case DATABASE_TYPE_CREATE_ATTRIBUTE: - $this->createAttribute($database, $collection, $document, $project); + createDBAttribute($database, $collection, $document, $project, $dbForProject); break; case DATABASE_TYPE_DELETE_ATTRIBUTE: - $this->deleteAttribute($database, $collection, $document, $project); + deleteDBAttribute($database, $collection, $document, $project, $dbForProject); break; case DATABASE_TYPE_CREATE_INDEX: - $this->createIndex($database, $collection, $document, $project); + createIndex($database, $collection, $document, $project, $dbForProject); break; case DATABASE_TYPE_DELETE_INDEX: - $this->deleteIndex($database, $collection, $document, $project); + deleteIndex($database, $collection, $document, $project, $dbForProject); break; default: Console::error('No database operation for type: ' . $type); break; } - } + }); - public function shutdown(): void - { - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $attribute - * @param Document $project - */ - protected function createAttribute(Document $database, Document $collection, Document $attribute, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); - /** - * Fetch attribute from the database, since with Resque float values are loosing informations. - */ - $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); - - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); - $size = $attribute->getAttribute('size', 0); - $required = $attribute->getAttribute('required', false); - $default = $attribute->getAttribute('default', null); - $signed = $attribute->getAttribute('signed', true); - $array = $attribute->getAttribute('array', false); - $format = $attribute->getAttribute('format', ''); - $formatOptions = $attribute->getAttribute('formatOptions', []); - $filters = $attribute->getAttribute('filters', []); - $project = $dbForConsole->getDocument('projects', $projectId); - - try { - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); - } - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project, - ); - - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $attribute - * @param Document $project - */ - protected function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $status = $attribute->getAttribute('status', ''); - $project = $dbForConsole->getDocument('projects', $projectId); - - // possible states at this point: - // - available: should not land in queue; controller flips these to 'deleting' - // - processing: hasn't finished creating - // - deleting: was available, in deletion queue for first time - // - failed: attribute was never created - // - stuck: attribute was available but cannot be removed - try { - if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new Exception('Failed to delete Attribute'); - } - $dbForProject->deleteDocument('attributes', $attribute->getId()); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - // The underlying database removes/rebuilds indexes when attribute is removed - // Update indexes table with changes - /** @var Document[] $indexes */ - $indexes = $collection->getAttribute('indexes', []); - - foreach ($indexes as $index) { - /** @var string[] $attributes */ - $attributes = $index->getAttribute('attributes'); - $lengths = $index->getAttribute('lengths'); - $orders = $index->getAttribute('orders'); - - $found = \array_search($key, $attributes); - - if ($found !== false) { - // If found, remove entry from attributes, lengths, and orders - // array_values wraps array_diff to reindex array keys - // when found attribute is removed from array - $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); - $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); - $orders = \array_values(\array_diff($orders, [$orders[$found]])); - - if (empty($attributes)) { - $dbForProject->deleteDocument('indexes', $index->getId()); - } else { - $index - ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) - ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) - ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN) - ; - - // Check if an index exists with the same attributes and orders - $exists = false; - foreach ($indexes as $existing) { - if ( - $existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself - && $existing->getAttribute('attributes') === $index->getAttribute('attributes') - && $existing->getAttribute('orders') === $index->getAttribute('orders') - ) { - $exists = true; - break; - } - } - - if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project); - } else { - $dbForProject->updateDocument('indexes', $index->getId(), $index); - } - } - } - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $index - * @param Document $project - */ - protected function createIndex(Document $database, Document $collection, Document $index, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); - $collectionId = $collection->getId(); - $key = $index->getAttribute('key', ''); - $type = $index->getAttribute('type', ''); - $attributes = $index->getAttribute('attributes', []); - $lengths = $index->getAttribute('lengths', []); - $orders = $index->getAttribute('orders', []); - $project = $dbForConsole->getDocument('projects', $projectId); - - try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { - throw new Exception('Failed to create Index'); - } - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $index - * @param Document $project - */ - protected function deleteIndex(Document $database, Document $collection, Document $index, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); - $key = $index->getAttribute('key'); - $status = $index->getAttribute('status', ''); - $project = $dbForConsole->getDocument('projects', $projectId); - - try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new Exception('Failed to delete index'); - } - $dbForProject->deleteDocument('indexes', $index->getId()); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); - } -} +$server->workerStart(); +$server->start(); diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 1822f06c71..2c6aaea926 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -4,6 +4,8 @@ namespace Appwrite\Event; use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Database extends Event { @@ -12,7 +14,7 @@ class Database extends Event protected ?Document $collection = null; protected ?Document $document = null; - public function __construct() + public function __construct(protected Connection $connection) { parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME); } @@ -104,7 +106,9 @@ class Database extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'type' => $this->type, From 02271c5329e9f149cb9e807e2c7bcb617abbc32e Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 13 Dec 2022 15:54:37 +0000 Subject: [PATCH 006/144] Remove debug force true --- src/Appwrite/Platform/Tasks/Maintenance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 87995b0e1e..9523312e08 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -92,7 +92,7 @@ class Maintenance extends Action ]); - if (\count($certificates) > 0 || true) { + if (\count($certificates) > 0) { Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); foreach ($certificates as $certificate) { From e4ad14c794917822b8f629b11493788685322bf2 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 13 Dec 2022 15:56:36 +0000 Subject: [PATCH 007/144] Run Linter --- app/workers/databases.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/workers/databases.php b/app/workers/databases.php index e5686203e1..a24111ed7f 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -47,7 +47,7 @@ function getCache(): Cache /** * Get Project DB - * + * * @param Document $project * @returns Database */ @@ -80,7 +80,7 @@ function getProjectDB(Document $project): Database * @param Document $collection * @param Document $attribute * @param Database $dbForConsole - * + * * @param Document $project */ function createDBAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole): void From 2e49666ee6e485ca6a71139a42df688cceab0a90 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Sat, 17 Dec 2022 19:19:41 +0000 Subject: [PATCH 008/144] Port Mail Worker from resque to Utopia Queue --- app/init.php | 4 +- app/workers/mails.php | 139 ++++++++++++++++++------------------ src/Appwrite/Event/Mail.php | 10 ++- 3 files changed, 79 insertions(+), 74 deletions(-) diff --git a/app/init.php b/app/init.php index 2f71a60ce8..7305117c5e 100644 --- a/app/init.php +++ b/app/init.php @@ -850,7 +850,9 @@ App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en'))) // Queues App::setResource('events', fn() => new Event('', '')); App::setResource('audits', fn() => new Audit()); -App::setResource('mails', fn() => new Mail()); +App::setResource('mails', function (Connection $queue) { + return new Mail($queue); +}, ['queue']); App::setResource('deletes', fn() => new Delete()); App::setResource('database', fn() => new EventDatabase()); App::setResource('messaging', fn() => new Phone()); diff --git a/app/workers/mails.php b/app/workers/mails.php index 90e9e9a660..7d2927d562 100644 --- a/app/workers/mails.php +++ b/app/workers/mails.php @@ -5,26 +5,69 @@ use Appwrite\Template\Template; use Utopia\App; use Utopia\CLI\Console; use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; use Utopia\Locale\Locale; +use Utopia\Queue\Message; +use Utopia\Queue\Server; -require_once __DIR__ . '/../init.php'; +require_once __DIR__ . '/../worker.php'; -Console::title('Mails V1 Worker'); -Console::success(APP_NAME . ' mails worker v1 has started' . "\n"); -class MailsV1 extends Worker +Authorization::disable(); +Authorization::setDefaultStatus(false); + +/* + * Returns a prefix from a mail type + * + * @param $type + * + * @return string + */ +function getPrefix(string $type): string { - public function getName(): string - { - return "mails"; + switch ($type) { + case MAIL_TYPE_RECOVERY: + return 'emails.recovery'; + case MAIL_TYPE_CERTIFICATE: + return 'emails.certificate'; + case MAIL_TYPE_INVITATION: + return 'emails.invitation'; + case MAIL_TYPE_VERIFICATION: + return 'emails.verification'; + case MAIL_TYPE_MAGIC_SESSION: + return 'emails.magicSession'; + default: + throw new Exception('Undefined Mail Type : ' . $type, 500); + } +} + +/** + * Returns true if all the required terms in a locale exist. False otherwise + * + * @param $locale + * @param $prefix + * + * @return bool + */ +function doesLocaleExist(Locale $locale, string $prefix): bool +{ + + if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { + return false; } - public function init(): void - { - } + return true; +} + +$server->job() + ->inject('message') + ->action(function (Message $message) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } - public function run(): void - { global $register; if (empty(App::getEnv('_APP_SMTP_HOST'))) { @@ -32,20 +75,19 @@ class MailsV1 extends Worker return; } - $project = new Document($this->args['project'] ?? []); - $user = new Document($this->args['user'] ?? []); - $team = new Document($this->args['team'] ?? []); - $payload = $this->args['payload'] ?? []; + $project = new Document($payload['project'] ?? []); + $user = new Document($payload['user'] ?? []); + $team = new Document($payload['team'] ?? []); - $recipient = $this->args['recipient']; - $url = $this->args['url']; - $name = $this->args['name']; - $type = $this->args['type']; - $prefix = $this->getPrefix($type); - $locale = new Locale($this->args['locale']); + $recipient = $payload['recipient']; + $url = $payload['url']; + $name = $payload['name']; + $type = $payload['type']; + $prefix = getPrefix($type); + $locale = new Locale($payload['locale']); $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); - if (!$this->doesLocaleExist($locale, $prefix)) { + if (!doesLocaleExist($locale, $prefix)) { $locale->setDefault('en'); } @@ -125,52 +167,7 @@ class MailsV1 extends Worker } catch (\Exception $error) { throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } - } + }); - public function shutdown(): void - { - } - - /** - * Returns a prefix from a mail type - * - * @param $type - * - * @return string - */ - protected function getPrefix(string $type): string - { - switch ($type) { - case MAIL_TYPE_RECOVERY: - return 'emails.recovery'; - case MAIL_TYPE_CERTIFICATE: - return 'emails.certificate'; - case MAIL_TYPE_INVITATION: - return 'emails.invitation'; - case MAIL_TYPE_VERIFICATION: - return 'emails.verification'; - case MAIL_TYPE_MAGIC_SESSION: - return 'emails.magicSession'; - default: - throw new Exception('Undefined Mail Type : ' . $type, 500); - } - } - - /** - * Returns true if all the required terms in a locale exist. False otherwise - * - * @param $locale - * @param $prefix - * - * @return bool - */ - protected function doesLocaleExist(Locale $locale, string $prefix): bool - { - - if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { - return false; - } - - return true; - } -} +$server->workerStart(); +$server->start(); diff --git a/src/Appwrite/Event/Mail.php b/src/Appwrite/Event/Mail.php index b3325c00be..cffee488f7 100644 --- a/src/Appwrite/Event/Mail.php +++ b/src/Appwrite/Event/Mail.php @@ -4,6 +4,8 @@ namespace Appwrite\Event; use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Mail extends Event { @@ -14,7 +16,7 @@ class Mail extends Event protected string $locale = ''; protected ?Document $team = null; - public function __construct() + public function __construct(protected Connection $connection) { parent::__construct(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME); } @@ -165,7 +167,11 @@ class Mail extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'payload' => $this->payload, From a8595eaca2c205cd5aa237d3ce3c297156a83a61 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Sat, 17 Dec 2022 20:45:58 +0000 Subject: [PATCH 009/144] Port Messages worker to Utopia queue system --- app/init.php | 5 +- app/workers/messaging.php | 99 ++++++++++++++++++++---------------- src/Appwrite/Event/Phone.php | 10 +++- 3 files changed, 68 insertions(+), 46 deletions(-) diff --git a/app/init.php b/app/init.php index 2f71a60ce8..ccd6832447 100644 --- a/app/init.php +++ b/app/init.php @@ -853,7 +853,10 @@ App::setResource('audits', fn() => new Audit()); App::setResource('mails', fn() => new Mail()); App::setResource('deletes', fn() => new Delete()); App::setResource('database', fn() => new EventDatabase()); -App::setResource('messaging', fn() => new Phone()); +App::setResource('messaging', function (Connection $queue) { + return new Phone($queue); +}, ['queue']); + App::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); diff --git a/app/workers/messaging.php b/app/workers/messaging.php index 5732c8c00b..8f6725fac7 100644 --- a/app/workers/messaging.php +++ b/app/workers/messaging.php @@ -1,7 +1,9 @@ getUser(); +$secret = $dsn->getPassword(); -class MessagingV1 extends Worker -{ - protected ?Adapter $sms = null; - protected ?string $from = null; +Server::setResource('sms', function () use ($dsn, $user, $secret) { + return match ($dsn->getHost()) { + 'mock' => new Mock($user, $secret), // used for tests + 'twilio' => new Twilio($user, $secret), + 'text-magic' => new TextMagic($user, $secret), + 'telesign' => new Telesign($user, $secret), + 'msg91' => new Msg91($user, $secret), + 'vonage' => new Vonage($user, $secret), + default => null + }; +}); - public function getName(): string - { - return "mails"; - } +Server::setResource('execute', function () { + return function (string $recipient, string $message, Adapter $sms) { + $from = App::getEnv('_APP_SMS_FROM'); - public function init(): void - { - $dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER')); - $user = $dsn->getUser(); - $secret = $dsn->getPassword(); - - $this->sms = match ($dsn->getHost()) { - 'mock' => new Mock($user, $secret), // used for tests - 'twilio' => new Twilio($user, $secret), - 'text-magic' => new TextMagic($user, $secret), - 'telesign' => new Telesign($user, $secret), - 'msg91' => new Msg91($user, $secret), - 'vonage' => new Vonage($user, $secret), - default => null - }; - - $this->from = App::getEnv('_APP_SMS_FROM'); - } - - public function run(): void - { if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { Console::info('Skipped sms processing. No Phone provider has been set.'); return; } - if (empty($this->from)) { + if (empty($from)) { Console::info('Skipped sms processing. No phone number has been set.'); return; } $message = new SMS( - to: [$this->args['recipient']], - content: $this->args['message'], - from: $this->from, + to: [$recipient], + content: $message, + from: $from, ); try { - $this->sms->send($message); + $sms->send($message); + Console::info('Successfully sent sms message to ' . $recipient); } catch (\Exception $error) { throw new Exception('Error sending message: ' . $error->getMessage(), 500); } - } + }; +}); - public function shutdown(): void - { - } -} +$server->job() + ->inject('message') + ->inject('execute') + ->inject('sms') + ->action(function (Message $message, callable $execute, Adapter $sms) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + if (empty($payload['recipient'])) { + throw new Exception('Missing recipient'); + } + + if (empty($payload['message'])) { + throw new Exception('Missing message'); + } + + $execute($payload['recipient'], $payload['message'], $sms); + }); + +$server->workerStart(); +$server->start(); diff --git a/src/Appwrite/Event/Phone.php b/src/Appwrite/Event/Phone.php index 8baa5120c9..162f1c92c6 100644 --- a/src/Appwrite/Event/Phone.php +++ b/src/Appwrite/Event/Phone.php @@ -3,13 +3,15 @@ namespace Appwrite\Event; use Resque; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Phone extends Event { protected string $recipient = ''; protected string $message = ''; - public function __construct() + public function __construct(protected Connection $connection) { parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME); } @@ -68,7 +70,11 @@ class Phone extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'payload' => $this->payload, From 50f82a276f4c086ae6a24550c2b82faadcc6d0c0 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Sat, 17 Dec 2022 20:46:34 +0000 Subject: [PATCH 010/144] Run Linter --- app/workers/messaging.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/workers/messaging.php b/app/workers/messaging.php index 8f6725fac7..1c2b96c10b 100644 --- a/app/workers/messaging.php +++ b/app/workers/messaging.php @@ -24,7 +24,7 @@ $dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER')); $user = $dsn->getUser(); $secret = $dsn->getPassword(); -Server::setResource('sms', function () use ($dsn, $user, $secret) { +Server::setResource('sms', function () use ($dsn, $user, $secret) { return match ($dsn->getHost()) { 'mock' => new Mock($user, $secret), // used for tests 'twilio' => new Twilio($user, $secret), @@ -58,7 +58,6 @@ Server::setResource('execute', function () { try { $sms->send($message); - Console::info('Successfully sent sms message to ' . $recipient); } catch (\Exception $error) { throw new Exception('Error sending message: ' . $error->getMessage(), 500); } From ab7c04074cd50dc018a9f6d3bfae8f7f693cc25f Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Sat, 17 Dec 2022 21:23:15 +0000 Subject: [PATCH 011/144] Update Phone.php --- src/Appwrite/Event/Phone.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Event/Phone.php b/src/Appwrite/Event/Phone.php index 162f1c92c6..0399e27e45 100644 --- a/src/Appwrite/Event/Phone.php +++ b/src/Appwrite/Event/Phone.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Resque; use Utopia\Queue\Client; use Utopia\Queue\Connection; From a09e23d0b99c822d881f194702f8501a96be451a Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 11:07:49 +0000 Subject: [PATCH 012/144] Start work on porting webhooks --- app/cli.php | 14 +++- app/init.php | 8 +- app/worker.php | 13 +++ app/workers/builds.php | 5 +- app/workers/functions.php | 12 ++- app/workers/webhooks.php | 87 +++++++++++---------- docker-compose.yml | 1 + src/Appwrite/Event/Delete.php | 5 +- src/Appwrite/Event/Event.php | 19 +++-- src/Appwrite/Event/Func.php | 2 +- src/Appwrite/Platform/Tasks/Maintenance.php | 60 +++++++------- tests/unit/Event/EventTest.php | 21 ++++- 12 files changed, 158 insertions(+), 89 deletions(-) diff --git a/app/cli.php b/app/cli.php index bfe7bfcefb..74c3a6061f 100644 --- a/app/cli.php +++ b/app/cli.php @@ -3,6 +3,7 @@ require_once __DIR__ . '/init.php'; require_once __DIR__ . '/controllers/general.php'; +use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; @@ -17,6 +18,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Logger\Log; use Utopia\Pools\Group; +use Utopia\Queue\Connection; use Utopia\Registry\Registry; Authorization::disable(); @@ -140,10 +142,18 @@ CLI::setResource('influxdb', function (Registry $register) { return $database; }, ['register']); -CLI::setResource('queueForFunctions', function (Group $pools) { - return new Func($pools->get('queue')->pop()->getResource()); +CLI::setResource('queue', function (Group $pools) { + return $pools->get('queue')->pop()->getResource(); }, ['pools']); +CLI::setResource('deletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); + +CLI::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); + CLI::setResource('logError', function (Registry $register) { return function (Throwable $error, string $namespace, string $action) use ($register) { $logger = $register->get('logger'); diff --git a/app/init.php b/app/init.php index 2f71a60ce8..aaca91835c 100644 --- a/app/init.php +++ b/app/init.php @@ -848,15 +848,19 @@ App::setResource('register', fn() => $register); App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en'))); // Queues -App::setResource('events', fn() => new Event('', '')); App::setResource('audits', fn() => new Audit()); App::setResource('mails', fn() => new Mail()); -App::setResource('deletes', fn() => new Delete()); +App::setResource('deletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); App::setResource('database', fn() => new EventDatabase()); App::setResource('messaging', fn() => new Phone()); App::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); +App::setResource('events', function (Connection $queue) { + return new Event('', '', $queue); +}, ['queue']); App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); diff --git a/app/worker.php b/app/worker.php index 8151381d4a..752f5df317 100644 --- a/app/worker.php +++ b/app/worker.php @@ -2,6 +2,7 @@ require_once __DIR__ . '/init.php'; +use Appwrite\Event\Event; use Appwrite\Event\Func; use Swoole\Runtime; use Utopia\App; @@ -85,6 +86,18 @@ Server::setResource('queueForFunctions', function (Registry $register) { ); }, ['register']); +Server::setResource('events', function (Registry $register) { + $pools = $register->get('pools'); + return new Event( + '', + '', + $pools + ->get('queue') + ->pop() + ->getResource() + ); +}, ['register']); + Server::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); diff --git a/app/workers/builds.php b/app/workers/builds.php index d26f07ab75..586663f183 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -121,7 +121,10 @@ class BuildsV1 extends Worker /** Trigger Webhook */ $deploymentModel = new Deployment(); - $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); + $pools = $register->get('pools'); + $connection = $pools->get('queue')->pop()->getResource(); + + $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $connection); $deploymentUpdate ->setProject($project) ->setEvent('functions.[functionId].deployments.[deploymentId].update') diff --git a/app/workers/functions.php b/app/workers/functions.php index 31e64a2bb4..adb4348488 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -20,6 +20,7 @@ use Utopia\Database\Permission; use Utopia\Database\Query; use Utopia\Database\Role; use Utopia\Database\Validator\Authorization; +use Utopia\Queue\Connection; use Utopia\Queue\Server; Authorization::disable(); @@ -39,6 +40,7 @@ Server::setResource('execute', function () { string $event = null, string $eventData = null, string $executionId = null, + Connection $queueConnection, ) { $user ??= new Document(); $functionId = $function->getId(); @@ -161,9 +163,11 @@ Server::setResource('execute', function () { $execution = $dbForProject->updateDocument('executions', $executionId, $execution); + $connection = $ + /** Trigger Webhook */ $executionModel = new Execution(); - $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); + $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $queueConnection); $executionUpdate ->setProject($project) ->setUser($user) @@ -225,8 +229,9 @@ $server->job() ->inject('dbForProject') ->inject('queueForFunctions') ->inject('statsd') + ->inject('queue') ->inject('execute') - ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Client $statsd, callable $execute) { + ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Client $statsd, Connection $queue, callable $execute) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -280,7 +285,8 @@ $server->job() user: $user, data: null, executionId: null, - jwt: null + jwt: null, + queueConnection: $queue ); Console::success('Triggered function: ' . $events[0]); } diff --git a/app/workers/webhooks.php b/app/workers/webhooks.php index 4048581c0c..75eeec19dc 100644 --- a/app/workers/webhooks.php +++ b/app/workers/webhooks.php @@ -1,48 +1,23 @@ args['events']; - $payload = json_encode($this->args['payload']); - $project = new Document($this->args['project']); - $user = new Document($this->args['user'] ?? []); - - foreach ($project->getAttribute('webhooks', []) as $webhook) { - if (array_intersect($webhook->getAttribute('events', []), $events)) { - $this->execute($events, $payload, $webhook, $user, $project); - } - } - - if (!empty($this->errors)) { - throw new Exception(\implode(" / \n\n", $this->errors)); - } - } - - protected function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void - { +Server::setResource('execute', function () { + return function (array $events, string $payload, Document $webhook, Document $user, Document $project): void { $url = \rawurldecode($webhook->getAttribute('url')); $signatureKey = $webhook->getAttribute('signatureKey'); $signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true)); @@ -85,14 +60,40 @@ class WebhooksV1 extends Worker } if (false === \curl_exec($ch)) { - $this->errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name'); + $errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name'); } \curl_close($ch); - } + }; +}); - public function shutdown(): void - { - $this->errors = []; - } -} + +$server->job() + ->inject('message') + ->inject('execute') + ->action(function (Message $message, callable $execute) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $events = $payload['events']; + $webhookPayload = json_encode($payload['payload']); + $project = new Document($payload['project']); + $user = new Document($payload['user'] ?? []); + + foreach ($project->getAttribute('webhooks', []) as $webhook) { + if (array_intersect($webhook->getAttribute('events', []), $events)) { + $execute($events, $webhookPayload, $webhook, $user, $project); + } + } + + if (!empty($errors)) { + throw new Exception(\implode(" / \n\n", $errors)); + } + }); + + +$server->workerStart(); +$server->start(); diff --git a/docker-compose.yml b/docker-compose.yml index 7620039b63..35c6975c6c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -580,6 +580,7 @@ services: - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_CONNECTIONS_QUEUE appwrite-usage: entrypoint: usage diff --git a/src/Appwrite/Event/Delete.php b/src/Appwrite/Event/Delete.php index d1519121a6..ad69172062 100644 --- a/src/Appwrite/Event/Delete.php +++ b/src/Appwrite/Event/Delete.php @@ -4,6 +4,7 @@ namespace Appwrite\Event; use Resque; use Utopia\Database\Document; +use Utopia\Queue\Connection; class Delete extends Event { @@ -14,9 +15,9 @@ class Delete extends Event protected ?string $hourlyUsageRetentionDatetime = null; - public function __construct() + public function __construct(protected Connection $connection) { - parent::__construct(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME); + parent::__construct(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, $connection); } /** diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index f88d2e94a6..bb269dcda4 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -3,8 +3,9 @@ namespace Appwrite\Event; use InvalidArgumentException; -use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Event { @@ -43,16 +44,18 @@ class Event protected array $context = []; protected ?Document $project = null; protected ?Document $user = null; + protected Connection $connection; /** * @param string $queue * @param string $class * @return void */ - public function __construct(string $queue, string $class) + public function __construct(string $queue, string $class, Connection $connection) { $this->queue = $queue; $this->class = $class; + $this->connection = $connection; } /** @@ -260,7 +263,11 @@ class Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'payload' => $this->payload, @@ -437,9 +444,9 @@ class Event if ($subCurrent === $current || $subCurrent === $key) { continue; } - $filtered1 = \array_filter($paramKeys, fn(string $k) => $k === $subCurrent); + $filtered1 = \array_filter($paramKeys, fn (string $k) => $k === $subCurrent); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered1, '*', $eventPattern)); - $filtered2 = \array_filter($paramKeys, fn(string $k) => $k === $current); + $filtered2 = \array_filter($paramKeys, fn (string $k) => $k === $current); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered2, '*', \str_replace($filtered1, '*', $eventPattern))); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered2, '*', $eventPattern)); } @@ -447,7 +454,7 @@ class Event if ($current === $key) { continue; } - $filtered = \array_filter($paramKeys, fn(string $k) => $k === $current); + $filtered = \array_filter($paramKeys, fn (string $k) => $k === $current); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered, '*', $eventPattern)); } } diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index 5f8b4c80c6..a632e3bfb0 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -16,7 +16,7 @@ class Func extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME); + parent::__construct(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, $connection); } /** diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 0739923e34..2b58aec513 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Auth\Auth; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; +use Appwrite\Event\Func; use Utopia\App; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -12,6 +13,8 @@ use Utopia\Database\Document; use Utopia\Database\DateTime; use Utopia\Database\Query; use Utopia\Platform\Action; +use Utopia\Queue\Connection; +use Utopia\Registry\Registry; class Maintenance extends Action { @@ -25,57 +28,58 @@ class Maintenance extends Action $this ->desc('Schedules maintenance tasks and publishes them to resque') ->inject('dbForConsole') - ->callback(fn (Database $dbForConsole) => $this->action($dbForConsole)); + ->inject('deletes') + ->callback(fn (Database $dbForConsole, Delete $deletes) => $this->action($dbForConsole, $deletes)); } - public function action(Database $dbForConsole): void + public function action(Database $dbForConsole, Delete $queue): void { Console::title('Maintenance V1'); Console::success(APP_NAME . ' maintenance process v1 has started'); - function notifyDeleteExecutionLogs(int $interval) + function notifyDeleteExecutionLogs(int $interval, Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_EXECUTIONS) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteAbuseLogs(int $interval) + function notifyDeleteAbuseLogs(int $interval, Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_ABUSE) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteAuditLogs(int $interval) + function notifyDeleteAuditLogs(int $interval, Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_AUDIT) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteUsageStats(int $usageStatsRetentionHourly) + function notifyDeleteUsageStats(int $usageStatsRetentionHourly, Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_USAGE) ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); } - function notifyDeleteConnections() + function notifyDeleteConnections(Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_REALTIME) ->setDatetime(DateTime::addSeconds(new \DateTime(), -60)) ->trigger(); } - function notifyDeleteExpiredSessions() + function notifyDeleteExpiredSessions(Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_SESSIONS) ->trigger(); } @@ -107,19 +111,19 @@ class Maintenance extends Action } } - function notifyDeleteCache($interval) + function notifyDeleteCache($interval, Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteSchedules($interval) + function notifyDeleteSchedules($interval, Delete $deletes) { - (new Delete()) + ($deletes) ->setType(DELETE_TYPE_SCHEDULES) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); @@ -135,19 +139,19 @@ class Maintenance extends Action $cacheRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days $schedulesDeletionRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day - Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole) { + Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole, $queue) { $time = DateTime::now(); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); - notifyDeleteExecutionLogs($executionLogsRetention); - notifyDeleteAbuseLogs($abuseLogsRetention); - notifyDeleteAuditLogs($auditLogRetention); - notifyDeleteUsageStats($usageStatsRetentionHourly); - notifyDeleteConnections(); - notifyDeleteExpiredSessions(); - renewCertificates($dbForConsole); - notifyDeleteCache($cacheRetention); - notifyDeleteSchedules($schedulesDeletionRetention); + notifyDeleteExecutionLogs($executionLogsRetention, $queue); + notifyDeleteAbuseLogs($abuseLogsRetention, $queue); + notifyDeleteAuditLogs($auditLogRetention, $queue); + notifyDeleteUsageStats($usageStatsRetentionHourly, $queue); + notifyDeleteConnections($queue); + notifyDeleteExpiredSessions($queue); + renewCertificates($dbForConsole, $queue); + notifyDeleteCache($cacheRetention, $queue); + notifyDeleteSchedules($schedulesDeletionRetention, $queue); }, $interval); } } diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index dee905638f..6a001fac1e 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -3,9 +3,12 @@ namespace Tests\Unit\Event; use Appwrite\Event\Event; +use Appwrite\URL\URL; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Utopia\App; +use Utopia\DSN\DSN; +use Utopia\Queue; class EventTest extends TestCase { @@ -18,8 +21,24 @@ class EventTest extends TestCase $redisPort = App::getEnv('_APP_REDIS_PORT', ''); \Resque::setBackend($redisHost . ':' . $redisPort); + $fallbackForRedis = URL::unparse([ + 'scheme' => 'redis', + 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => App::getEnv('_APP_REDIS_USER', ''), + 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + ]); + + $dsn = App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis); + $dsn = explode('=', $dsn); + $dsn = $dsn[1] ?? ''; + + $dsn = new DSN($dsn); + + $connection = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); + $this->queue = 'v1-tests' . uniqid(); - $this->object = new Event($this->queue, 'TestsV1'); + $this->object = new Event($this->queue, 'TestsV1', $connection); } public function testQueue(): void From 3e66d1a549e40c2876b56ded97ebe1ca9504264d Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 11:21:07 +0000 Subject: [PATCH 013/144] Rename certificate queue --- app/controllers/api/projects.php | 2 +- app/controllers/general.php | 2 +- app/init.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 87bba655c5..d307c41dbc 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1314,7 +1314,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') - ->inject('queueForCertificates') + ->inject('certificates') ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Certificate $queueForCertificates) { $project = $dbForConsole->getDocument('projects', $projectId); diff --git a/app/controllers/general.php b/app/controllers/general.php index 1e894d087a..fb41689487 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -51,7 +51,7 @@ App::init() ->inject('locale') ->inject('clients') ->inject('servers') - ->inject('queueForCertificates') + ->inject('certificates') ->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients, array $servers, Certificate $queueForCertificates) { /* * Request format diff --git a/app/init.php b/app/init.php index 2f5aeaa77f..3a2f3b40b9 100644 --- a/app/init.php +++ b/app/init.php @@ -870,7 +870,7 @@ App::setResource('events', function (Connection $queue) { App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { +App::setResource('certificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); App::setResource('usage', function ($register) { From 886a1a2abb37b73a5d0ea807cd9bd50ee225a0aa Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 12:22:58 +0000 Subject: [PATCH 014/144] Port Deletes and Builds worker --- app/cli.php | 43 +- app/controllers/api/account.php | 1 - app/controllers/api/functions.php | 12 +- app/init.php | 10 +- app/worker.php | 165 ++++- app/workers/builds.php | 436 ++++++------ app/workers/databases.php | 50 -- app/workers/deletes.php | 1009 ++++++++++++++-------------- src/Appwrite/Event/Audit.php | 3 +- src/Appwrite/Event/Build.php | 13 +- src/Appwrite/Event/Certificate.php | 3 +- src/Appwrite/Event/Database.php | 3 +- src/Appwrite/Event/Delete.php | 8 +- src/Appwrite/Event/Mail.php | 3 +- src/Appwrite/Event/Phone.php | 2 +- 15 files changed, 933 insertions(+), 828 deletions(-) diff --git a/app/cli.php b/app/cli.php index acadd10be1..4106ae35b5 100644 --- a/app/cli.php +++ b/app/cli.php @@ -8,6 +8,10 @@ use Appwrite\Event\Certificate; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Func; use Appwrite\Event\Audit; +use Appwrite\Event\Build; +use Appwrite\Event\Event; +use Appwrite\Event\Mail; +use Appwrite\Event\Phone; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; use Utopia\Database\Validator\Authorization; @@ -149,27 +153,36 @@ CLI::setResource('influxdb', function (Registry $register) { CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); - -CLI::setResource('deletes', function (Connection $queue) { - return new Delete($queue); +CLI::setResource('messaging', function (Connection $queue) { + return new Phone($queue); }, ['queue']); - -CLI::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); +CLI::setResource('mails', function (Connection $queue) { + return new Mail($queue); }, ['queue']); - -CLI::setResource('audits', function (Connection $queue) { - return new Audit($queue); +CLI::setResource('builds', function (Connection $queue) { + return new Build($queue); }, ['queue']); - -CLI::setResource('certificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); - CLI::setResource('database', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); - +CLI::setResource('deletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); +CLI::setResource('events', function (Connection $queue) { + return new Event('', '', $queue); +}, ['queue']); +CLI::setResource('audits', function (Connection $queue) { + return new Audit($queue); +}, ['queue']); +CLI::setResource('events', function (Connection $queue) { + return new Event('', '', $queue); +}, ['queue']); +CLI::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); +CLI::setResource('certificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); CLI::setResource('logError', function (Registry $register) { return function (Throwable $error, string $namespace, string $action) use ($register) { $logger = $register->get('logger'); diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 346820d699..3d6e7bf762 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1291,7 +1291,6 @@ App::get('/v1/account') ->inject('response') ->inject('user') ->action(function (Response $response, Document $user) { - $response->dynamic($user, Response::MODEL_ACCOUNT); }); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 6fe7c67e69..88b91ab8cd 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -634,7 +634,8 @@ App::post('/v1/functions/:functionId/deployments') ->inject('deviceFunctions') ->inject('deviceLocal') ->inject('dbForConsole') - ->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole) { + ->inject('builds') + ->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole, Build $builds) { $function = $dbForProject->getDocument('functions', $functionId); @@ -754,8 +755,7 @@ App::post('/v1/functions/:functionId/deployments') } // Start the build - $buildEvent = new Build(); - $buildEvent + $builds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) ->setDeployment($deployment) @@ -1001,7 +1001,8 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->inject('dbForProject') ->inject('project') ->inject('events') - ->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events) { + ->inject('builds') + ->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events, Build $builds) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -1029,8 +1030,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->setParam('deploymentId', $deployment->getId()); // Retry the build - $buildEvent = new Build(); - $buildEvent + $builds ->setType(BUILD_TYPE_RETRY) ->setResource($function) ->setDeployment($deployment) diff --git a/app/init.php b/app/init.php index bb2b6f2a6d..bea032ab75 100644 --- a/app/init.php +++ b/app/init.php @@ -69,6 +69,7 @@ use Utopia\Pools\Group; use Utopia\Pools\Pool; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; +use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Func; use MaxMind\Db\Reader; @@ -858,18 +859,21 @@ App::setResource('messaging', function (Connection $queue) { App::setResource('mails', function (Connection $queue) { return new Mail($queue); }, ['queue']); +App::setResource('builds', function (Connection $queue) { + return new Build($queue); +}, ['queue']); App::setResource('database', function (Connection $queue) { return new EventDatabase($queue); -}, ['pools']); +}, ['queue']); App::setResource('deletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); App::setResource('events', function (Connection $queue) { return new Event('', '', $queue); -}, ['pools']); +}, ['queue']); App::setResource('audits', function (Connection $queue) { return new Audit($queue); -}, ['pools']); +}, ['queue']); App::setResource('events', function (Connection $queue) { return new Event('', '', $queue); }, ['queue']); diff --git a/app/worker.php b/app/worker.php index 21785362d2..33fe128a67 100644 --- a/app/worker.php +++ b/app/worker.php @@ -4,9 +4,13 @@ require_once __DIR__ . '/init.php'; use Appwrite\Event\Event; use Appwrite\Event\Audit; +use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Database as EventDatabase; +use Appwrite\Event\Delete; use Appwrite\Event\Func; +use Appwrite\Event\Mail; +use Appwrite\Event\Phone; use Swoole\Runtime; use Utopia\App; use Utopia\Cache\Adapter\Sharding; @@ -15,6 +19,7 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\DSN\DSN; use Utopia\Queue\Adapter\Swoole; use Utopia\Queue\Message; use Utopia\Queue\Server; @@ -22,6 +27,15 @@ use Utopia\Registry\Registry; use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Pools\Group; +use Utopia\Queue\Connection; +use Utopia\Storage\Device; +use Utopia\Storage\Device\Backblaze; +use Utopia\Storage\Device\DOSpaces; +use Utopia\Storage\Device\Linode; +use Utopia\Storage\Device\Local; +use Utopia\Storage\Device\S3; +use Utopia\Storage\Device\Wasabi; +use Utopia\Storage\Storage; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); @@ -86,16 +100,39 @@ Server::setResource('database', function (Registry $register) { ); }, ['register']); -Server::setResource('queueForFunctions', function (Registry $register) { - $pools = $register->get('pools'); - return new Func( - $pools - ->get('queue') - ->pop() - ->getResource() - ); -}, ['register']); - +Server::setResource('queue', function (Group $pools) { + return $pools->get('queue')->pop()->getResource(); +}, ['pools']); +Server::setResource('messaging', function (Connection $queue) { + return new Phone($queue); +}, ['queue']); +Server::setResource('mails', function (Connection $queue) { + return new Mail($queue); +}, ['queue']); +Server::setResource('builds', function (Connection $queue) { + return new Build($queue); +}, ['queue']); +Server::setResource('database', function (Connection $queue) { + return new EventDatabase($queue); +}, ['queue']); +Server::setResource('deletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); +Server::setResource('events', function (Connection $queue) { + return new Event('', '', $queue); +}, ['queue']); +Server::setResource('audits', function (Connection $queue) { + return new Audit($queue); +}, ['queue']); +Server::setResource('events', function (Connection $queue) { + return new Event('', '', $queue); +}, ['queue']); +Server::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); +Server::setResource('certificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); Server::setResource('events', function (Registry $register) { $pools = $register->get('pools'); return new Event( @@ -197,3 +234,111 @@ $server Console::error('[Error] File: ' . $error->getFile()); Console::error('[Error] Line: ' . $error->getLine()); }); + +/** + * Get Console DB + * + * @returns Cache + */ +function getCache(): Cache +{ + global $register; + + $pools = $register->get('pools'); + /** @var \Utopia\Pools\Group $pools */ + + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource(); + } + + return new Cache(new Sharding($adapters)); +} + +/** + * Get Console DB + * + * @returns Database + */ +function getConsoleDB(): Database +{ + global $register; + + /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); + + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, getCache()); + + $database->setNamespace('console'); + + return $database; +} + +/** + * Get Project DB + * + * @param Document $project + * @returns Database + */ +function getProjectDB(Document $project): Database +{ + global $register; + + /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); + + if ($project->isEmpty() || $project->getId() === 'console') { + return getConsoleDB(); + } + + $dbAdapter = $pools + ->get($project->getAttribute('database')) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, getCache()); + $database->setNamespace('_' . $project->getInternalId()); + + return $database; +} + + +/** + * Get Functions Storage Device + * @param string $projectId of the project + * @return Device + */ +function getFunctionsDevice($projectId): Device +{ + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); +} + +/** + * Get Files Storage Device + * @param string $projectId of the project + * @return Device + */ +function getFilesDevice($projectId): Device +{ + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); +} + +/** + * Get Builds Storage Device + * @param string $projectId of the project + * @return Device + */ +function getBuildsDevice($projectId): Device +{ + return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); +} diff --git a/app/workers/builds.php b/app/workers/builds.php index 586663f183..9ca077dfcd 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -1,5 +1,7 @@ getDocument('functions', $function->getId()); + if ($function->isEmpty()) { + throw new Exception('Function not found', 404); } - public function init(): void - { - $this->executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); + if ($deployment->isEmpty()) { + throw new Exception('Deployment not found', 404); } - public function run(): void - { - $type = $this->args['type'] ?? ''; - $project = new Document($this->args['project'] ?? []); - $resource = new Document($this->args['resource'] ?? []); - $deployment = new Document($this->args['deployment'] ?? []); - - switch ($type) { - case BUILD_TYPE_DEPLOYMENT: - case BUILD_TYPE_RETRY: - Console::info('Creating build for deployment: ' . $deployment->getId()); - $this->buildDeployment($project, $resource, $deployment); - break; - - default: - throw new \Exception('Invalid build type'); - break; - } + $runtimes = Config::getParam('runtimes', []); + $key = $function->getAttribute('runtime'); + $runtime = isset($runtimes[$key]) ? $runtimes[$key] : null; + if (\is_null($runtime)) { + throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - protected function buildDeployment(Document $project, Document $function, Document $deployment) - { - global $register; + $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); /** @TODO : move this to the registry or someplace else */ + $device = Storage::DEVICE_LOCAL; + try { + $dsn = new DSN($connection); + $device = $dsn->getScheme(); + } catch (\Exception $e) { + Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } - $dbForProject = $this->getProjectDB($project); + $buildId = $deployment->getAttribute('buildId', ''); + $startTime = DateTime::now(); + if (empty($buildId)) { + $buildId = ID::unique(); + $build = $dbForProject->createDocument('builds', new Document([ + '$id' => $buildId, + '$permissions' => [], + 'startTime' => $startTime, + 'deploymentId' => $deployment->getId(), + 'status' => 'processing', + 'outputPath' => '', + 'runtime' => $function->getAttribute('runtime'), + 'source' => $deployment->getAttribute('path'), + 'sourceType' => $device, + 'stdout' => '', + 'stderr' => '', + 'endTime' => null, + 'duration' => 0 + ])); + $deployment->setAttribute('buildId', $buildId); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + } else { + $build = $dbForProject->getDocument('builds', $buildId); + } - $function = $dbForProject->getDocument('functions', $function->getId()); - if ($function->isEmpty()) { - throw new Exception('Function not found', 404); + /** Request the executor to build the code... */ + $build->setAttribute('status', 'building'); + $build = $dbForProject->updateDocument('builds', $buildId, $build); + + /** Trigger Webhook */ + $deploymentModel = new Deployment(); + + $pools = $register->get('pools'); + $connection = $pools->get('queue')->pop()->getResource(); + + $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $connection); + $deploymentUpdate + ->setProject($project) + ->setEvent('functions.[functionId].deployments.[deploymentId].update') + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules()))) + ->trigger(); + + /** Trigger Functions */ + $pools = $register->get('pools'); + $connection = $pools->get('queue')->pop(); + + $functions = new Func($connection->getResource()); + $functions + ->from($deploymentUpdate) + ->trigger(); + + $connection->reclaim(); + + /** Trigger Realtime */ + $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ + 'functionId' => $function->getId(), + 'deploymentId' => $deployment->getId() + ]); + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + + $source = $deployment->getAttribute('path'); + + $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { + $carry[$var->getAttribute('key')] = $var->getAttribute('value'); + return $carry; + }, []); + + try { + $response = $executor->createRuntime( + projectId: $project->getId(), + deploymentId: $deployment->getId(), + source: $source, + image: $runtime['image'], + remove: true, + entrypoint: $deployment->getAttribute('entrypoint'), + workdir: '/usr/code', + destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", + variables: $vars, + commands: [ + 'sh', '-c', + 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ + cd /usr/local/src/ && ./build.sh' + ] + ); + + $endTime = new \DateTime(); + $endTime->setTimestamp($response['endTimeUnix']); + + /** Update the build document */ + $build->setAttribute('endTime', DateTime::format($endTime)); + $build->setAttribute('duration', \intval($response['duration'])); + $build->setAttribute('status', $response['status']); + $build->setAttribute('outputPath', $response['outputPath']); + $build->setAttribute('stderr', $response['stderr']); + $build->setAttribute('stdout', $response['stdout']); + + /* Also update the deployment buildTime */ + $deployment->setAttribute('buildTime', $response['duration']); + + Console::success("Build id: $buildId created"); + + $function->setAttribute('scheduleUpdatedAt', DateTime::now()); + + /** Set auto deploy */ + if ($deployment->getAttribute('activate') === true) { + $function->setAttribute('deployment', $deployment->getId()); + $function = $dbForProject->updateDocument('functions', $function->getId(), $function); } - $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); - if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404); - } + /** Update function schedule */ + $dbForConsole = getConsoleDB(); + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); - $runtimes = Config::getParam('runtimes', []); - $key = $function->getAttribute('runtime'); - $runtime = isset($runtimes[$key]) ? $runtimes[$key] : null; - if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); - } + $schedule + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); /** @TODO : move this to the registry or someplace else */ - $device = Storage::DEVICE_LOCAL; - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - } catch (\Exception $e) { - Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - $buildId = $deployment->getAttribute('buildId', ''); - $startTime = DateTime::now(); - if (empty($buildId)) { - $buildId = ID::unique(); - $build = $dbForProject->createDocument('builds', new Document([ - '$id' => $buildId, - '$permissions' => [], - 'startTime' => $startTime, - 'deploymentId' => $deployment->getId(), - 'status' => 'processing', - 'outputPath' => '', - 'runtime' => $function->getAttribute('runtime'), - 'source' => $deployment->getAttribute('path'), - 'sourceType' => $device, - 'stdout' => '', - 'stderr' => '', - 'endTime' => null, - 'duration' => 0 - ])); - $deployment->setAttribute('buildId', $buildId); - $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); - } else { - $build = $dbForProject->getDocument('builds', $buildId); - } - - /** Request the executor to build the code... */ - $build->setAttribute('status', 'building'); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + } catch (\Throwable $th) { + $endTime = DateTime::now(); + $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); + $build->setAttribute('endTime', $endTime); + $build->setAttribute('duration', $interval->format('%s') + 0); + $build->setAttribute('status', 'failed'); + $build->setAttribute('stderr', $th->getMessage()); + Console::error($th->getMessage()); + } finally { $build = $dbForProject->updateDocument('builds', $buildId, $build); - /** Trigger Webhook */ - $deploymentModel = new Deployment(); - - $pools = $register->get('pools'); - $connection = $pools->get('queue')->pop()->getResource(); - - $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $connection); - $deploymentUpdate - ->setProject($project) - ->setEvent('functions.[functionId].deployments.[deploymentId].update') - ->setParam('functionId', $function->getId()) - ->setParam('deploymentId', $deployment->getId()) - ->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules()))) - ->trigger(); - - /** Trigger Functions */ - $pools = $register->get('pools'); - $connection = $pools->get('queue')->pop(); - - $functions = new Func($connection->getResource()); - $functions - ->from($deploymentUpdate) - ->trigger(); - - $connection->reclaim(); - - /** Trigger Realtime */ - $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ - 'functionId' => $function->getId(), - 'deploymentId' => $deployment->getId() - ]); + /** + * Send realtime Event + */ $target = Realtime::fromPayload( // Pass first, most verbose event pattern event: $allEvents[0], payload: $build, project: $project ); - Realtime::send( projectId: 'console', payload: $build->getArrayCopy(), @@ -164,113 +219,50 @@ class BuildsV1 extends Worker roles: $target['roles'] ); - $source = $deployment->getAttribute('path'); - - $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { - $carry[$var->getAttribute('key')] = $var->getAttribute('value'); - return $carry; - }, []); - - try { - $response = $this->executor->createRuntime( - projectId: $project->getId(), - deploymentId: $deployment->getId(), - source: $source, - image: $runtime['image'], - remove: true, - entrypoint: $deployment->getAttribute('entrypoint'), - workdir: '/usr/code', - destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", - variables: $vars, - commands: [ - 'sh', '-c', - 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ - cd /usr/local/src/ && ./build.sh' - ] - ); - - $endTime = new \DateTime(); - $endTime->setTimestamp($response['endTimeUnix']); - - /** Update the build document */ - $build->setAttribute('endTime', DateTime::format($endTime)); - $build->setAttribute('duration', \intval($response['duration'])); - $build->setAttribute('status', $response['status']); - $build->setAttribute('outputPath', $response['outputPath']); - $build->setAttribute('stderr', $response['stderr']); - $build->setAttribute('stdout', $response['stdout']); - - /* Also update the deployment buildTime */ - $deployment->setAttribute('buildTime', $response['duration']); - - Console::success("Build id: $buildId created"); - - $function->setAttribute('scheduleUpdatedAt', DateTime::now()); - - /** Set auto deploy */ - if ($deployment->getAttribute('activate') === true) { - $function->setAttribute('deployment', $deployment->getId()); - $function = $dbForProject->updateDocument('functions', $function->getId(), $function); - } - - /** Update function schedule */ - $dbForConsole = $this->getConsoleDB(); - $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); - - $schedule - ->setAttribute('schedule', $function->getAttribute('schedule')) - ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - - - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - } catch (\Throwable $th) { - $endTime = DateTime::now(); - $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); - $build->setAttribute('endTime', $endTime); - $build->setAttribute('duration', $interval->format('%s') + 0); - $build->setAttribute('status', 'failed'); - $build->setAttribute('stderr', $th->getMessage()); - Console::error($th->getMessage()); - } finally { - $build = $dbForProject->updateDocument('builds', $buildId, $build); - - /** - * Send realtime Event - */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - - /** Update usage stats */ - if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { - $statsd = $register->get('statsd'); - $usage = new Stats($statsd); - $usage - ->setParam('projectInternalId', $project->getInternalId()) - ->setParam('projectId', $project->getId()) - ->setParam('functionId', $function->getId()) - ->setParam('builds.{scope}.compute', 1) - ->setParam('buildStatus', $build->getAttribute('status', '')) - ->setParam('buildTime', $build->getAttribute('duration')) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0) - ->submit(); - } + /** Update usage stats */ + if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { + $statsd = $register->get('statsd'); + $usage = new Stats($statsd); + $usage + ->setParam('projectInternalId', $project->getInternalId()) + ->setParam('projectId', $project->getId()) + ->setParam('functionId', $function->getId()) + ->setParam('builds.{scope}.compute', 1) + ->setParam('buildStatus', $build->getAttribute('status', '')) + ->setParam('buildTime', $build->getAttribute('duration')) + ->setParam('networkRequestSize', 0) + ->setParam('networkResponseSize', 0) + ->submit(); } } - - public function shutdown(): void - { - } } + +$server->job() + ->inject('message') + ->action(function (Message $message) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $type = $payload['type'] ?? ''; + $project = new Document($payload['project'] ?? []); + $resource = new Document($payload['resource'] ?? []); + $deployment = new Document($payload['deployment'] ?? []); + + switch ($type) { + case BUILD_TYPE_DEPLOYMENT: + case BUILD_TYPE_RETRY: + Console::info('Creating build for deployment: ' . $deployment->getId()); + buildDeployment($project, $resource, $deployment); + break; + + default: + throw new \Exception('Invalid build type'); + break; + } + }); + +$server->workerStart(); +$server->start(); diff --git a/app/workers/databases.php b/app/workers/databases.php index a24111ed7f..b0087e2913 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -25,56 +25,6 @@ Authorization::setDefaultStatus(false); const DATABASE_PROJECT = 'project'; const DATABASE_CONSOLE = 'console'; -function getCache(): Cache -{ - global $register; - - $pools = $register->get('pools'); - /** @var \Utopia\Pools\Group $pools */ - - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); - } - - return new Cache(new Sharding($adapters)); -} - -/** - * Get Project DB - * - * @param Document $project - * @returns Database - */ -function getProjectDB(Document $project): Database -{ - global $register; - - /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools'); - - if ($project->isEmpty() || $project->getId() === 'console') { - return getConsoleDB(); - } - - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource() - ; - - $database = new Database($dbAdapter, getCache()); - $database->setNamespace('_' . $project->getInternalId()); - - return $database; -} - - /** * @param Document $database * @param Document $collection diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 458b341a45..e546c314b9 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -1,5 +1,7 @@ job() + ->inject('message') + ->action(function (Message $message) { + $payload = $message->getPayload() ?? []; -class DeletesV1 extends Worker -{ - public function getName(): string - { - return "deletes"; - } + if (empty($payload)) { + throw new Exception('Missing payload'); + } - public function init(): void - { - } - - public function run(): void - { - $project = new Document($this->args['project'] ?? []); - $type = $this->args['type'] ?? ''; + $project = new Document($payload['project'] ?? []); + $type = $payload['type'] ?? ''; switch (strval($type)) { case DELETE_TYPE_DOCUMENT: - $document = new Document($this->args['document'] ?? []); + $document = new Document($payload['document'] ?? []); switch ($document->getCollection()) { case DELETE_TYPE_DATABASES: - $this->deleteDatabase($document, $project); + deleteDatabase($document, $project); break; case DELETE_TYPE_COLLECTIONS: - $this->deleteCollection($document, $project); + deleteCollection($document, $project); break; case DELETE_TYPE_PROJECTS: - $this->deleteProject($document); + deleteProject($document); break; case DELETE_TYPE_FUNCTIONS: - $this->deleteFunction($document, $project); + deleteFunction($document, $project); break; case DELETE_TYPE_DEPLOYMENTS: - $this->deleteDeployment($document, $project); + deleteDeployment($document, $project); break; case DELETE_TYPE_USERS: - $this->deleteUser($document, $project); + deleteUser($document, $project); break; case DELETE_TYPE_TEAMS: - $this->deleteMemberships($document, $project); + deleteMemberships($document, $project); break; case DELETE_TYPE_BUCKETS: - $this->deleteBucket($document, $project); + deleteBucket($document, $project); break; default: Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); @@ -72,479 +70,476 @@ class DeletesV1 extends Worker break; case DELETE_TYPE_EXECUTIONS: - $this->deleteExecutionLogs($this->args['datetime']); + deleteExecutionLogs($payload['datetime']); break; case DELETE_TYPE_AUDIT: - $datetime = $this->args['datetime'] ?? null; + $datetime = $payload['datetime'] ?? null; if (!empty($datetime)) { - $this->deleteAuditLogs($datetime); + deleteAuditLogs($datetime); } - $document = new Document($this->args['document'] ?? []); + $document = new Document($payload['document'] ?? []); if (!$document->isEmpty()) { - $this->deleteAuditLogsByResource('document/' . $document->getId(), $project); + deleteAuditLogsByResource('document/' . $document->getId(), $project); } break; case DELETE_TYPE_ABUSE: - $this->deleteAbuseLogs($this->args['datetime']); + deleteAbuseLogs($payload['datetime']); break; case DELETE_TYPE_REALTIME: - $this->deleteRealtimeUsage($this->args['datetime']); + deleteRealtimeUsage($payload['datetime']); break; case DELETE_TYPE_SESSIONS: - $this->deleteExpiredSessions(); + deleteExpiredSessions(); break; case DELETE_TYPE_CERTIFICATES: - $document = new Document($this->args['document']); - $this->deleteCertificates($document); + $document = new Document($payload['document']); + deleteCertificates($document); break; case DELETE_TYPE_USAGE: - $this->deleteUsageStats($this->args['hourlyUsageRetentionDatetime']); + deleteUsageStats($payload['hourlyUsageRetentionDatetime']); break; case DELETE_TYPE_CACHE_BY_RESOURCE: - $this->deleteCacheByResource($this->args['resource']); + deleteCacheByResource($payload['resource']); break; case DELETE_TYPE_CACHE_BY_TIMESTAMP: - $this->deleteCacheByDate(); + deleteCacheByDate($payload); break; case DELETE_TYPE_SCHEDULES: - $this->deleteSchedules($this->args['datetime']); + deleteSchedules($payload['datetime']); break; default: Console::error('No delete operation for type: ' . $type); break; } - } + }); - public function shutdown(): void - { - } /** * @throws Exception */ - protected function deleteSchedules(string $datetime): void - { - $this->listByGroup( - 'schedules', - [ - Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), - Query::equal('resourceType', ['function']), - Query::lessThanEqual('resourceUpdatedAt', $datetime), - Query::equal('active', [false]), - ], - $this->getConsoleDB(), - function (Document $document) { - $project = $this->getConsoleDB()->getDocument('projects', $document->getAttribute('projectId')); +function deleteSchedules(string $datetime): void +{ + listByGroup( + 'schedules', + [ + Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), + Query::equal('resourceType', ['function']), + Query::lessThanEqual('resourceUpdatedAt', $datetime), + Query::equal('active', [false]), + ], + getConsoleDB(), + function (Document $document) { + $project = getConsoleDB()->getDocument('projects', $document->getAttribute('projectId')); - if ($project->isEmpty()) { - Console::warning('Unable to delete schedule for function ' . $document->getAttribute('resourceId')); - return; - } - - $function = $this->getProjectDB($project)->getDocument('functions', $document->getAttribute('resourceId')); - - if ($function->isEmpty()) { - $this->getConsoleDB()->deleteDocument('schedules', $document->getId()); - Console::success('Deleting schedule for function ' . $document->getAttribute('resourceId')); - } + if ($project->isEmpty()) { + Console::warning('Unable to delete schedule for function ' . $document->getAttribute('resourceId')); + return; } - ); - } + + $function = getProjectDB($project)->getDocument('functions', $document->getAttribute('resourceId')); + + if ($function->isEmpty()) { + getConsoleDB()->deleteDocument('schedules', $document->getId()); + Console::success('Deleting schedule for function ' . $document->getAttribute('resourceId')); + } + } + ); +} /** * @param string $resource */ - protected function deleteCacheByResource(string $resource): void - { - $this->deleteCacheFiles([ - Query::equal('resource', [$resource]), - ]); - } +function deleteCacheByResource(string $resource): void +{ + deleteCacheFiles([ + Query::equal('resource', [$resource]), + ]); +} - protected function deleteCacheByDate(): void - { - $this->deleteCacheFiles([ - Query::lessThan('accessedAt', $this->args['datetime']), - ]); - } +function deleteCacheByDate($payload): void +{ + deleteCacheFiles([ + Query::lessThan('accessedAt', $payload['datetime']), + ]); +} - protected function deleteCacheFiles($query): void - { - $this->deleteForProjectIds(function (Document $project) use ($query) { +function deleteCacheFiles($query): void +{ + deleteForProjectIds(function (Document $project) use ($query) { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $cache = new Cache( - new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) - ); + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $cache = new Cache( + new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) + ); - $this->deleteByGroup( - 'cache', - $query, - $dbForProject, - function (Document $document) use ($cache, $projectId) { - $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); + deleteByGroup( + 'cache', + $query, + $dbForProject, + function (Document $document) use ($cache, $projectId) { + $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); - if ($cache->purge($document->getId())) { - Console::success('Deleting cache file: ' . $path); - } else { - Console::error('Failed to delete cache file: ' . $path); - } + if ($cache->purge($document->getId())) { + Console::success('Deleting cache file: ' . $path); + } else { + Console::error('Failed to delete cache file: ' . $path); } - ); - }); - } + } + ); + }); +} /** * @param Document $document database document * @param Document $projectId */ - protected function deleteDatabase(Document $document, Document $project): void - { - $databaseId = $document->getId(); - $projectId = $project->getId(); +function deleteDatabase(Document $document, Document $project): void +{ + $databaseId = $document->getId(); + $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); + $dbForProject = getProjectDB($project); - $this->deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($project) { - $this->deleteCollection($document, $project); - }); + deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($project) { + deleteCollection($document, $project); + }); - $dbForProject->deleteCollection('database_' . $document->getInternalId()); + $dbForProject->deleteCollection('database_' . $document->getInternalId()); - $this->deleteAuditLogsByResource('database/' . $databaseId, $project); - } + deleteAuditLogsByResource('database/' . $databaseId, $project); +} /** * @param Document $document teams document * @param Document $project */ - protected function deleteCollection(Document $document, Document $project): void - { - $collectionId = $document->getId(); - $databaseId = $document->getAttribute('databaseId'); - $databaseInternalId = $document->getAttribute('databaseInternalId'); +function deleteCollection(Document $document, Document $project): void +{ + $collectionId = $document->getId(); + $databaseId = $document->getAttribute('databaseId'); + $databaseInternalId = $document->getAttribute('databaseInternalId'); - $dbForProject = $this->getProjectDB($project); + $dbForProject = getProjectDB($project); - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); - $this->deleteByGroup('attributes', [ - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]) - ], $dbForProject); + deleteByGroup('attributes', [ + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$collectionId]) + ], $dbForProject); - $this->deleteByGroup('indexes', [ - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]) - ], $dbForProject); + deleteByGroup('indexes', [ + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$collectionId]) + ], $dbForProject); - $this->deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project); - } + deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project); +} /** * @param string $hourlyUsageRetentionDatetime */ - protected function deleteUsageStats(string $hourlyUsageRetentionDatetime) - { - $this->deleteForProjectIds(function (Document $project) use ($hourlyUsageRetentionDatetime) { - $dbForProject = $this->getProjectDB($project); - // Delete Usage stats - $this->deleteByGroup('stats', [ - Query::lessThan('time', $hourlyUsageRetentionDatetime), - Query::equal('period', ['1h']), - ], $dbForProject); - }); - } +function deleteUsageStats(string $hourlyUsageRetentionDatetime) +{ + deleteForProjectIds(function (Document $project) use ($hourlyUsageRetentionDatetime) { + $dbForProject = getProjectDB($project); + // Delete Usage stats + deleteByGroup('stats', [ + Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::equal('period', ['1h']), + ], $dbForProject); + }); +} /** * @param Document $document teams document * @param Document $project */ - protected function deleteMemberships(Document $document, Document $project): void - { - $teamId = $document->getAttribute('teamId', ''); +function deleteMemberships(Document $document, Document $project): void +{ + $teamId = $document->getAttribute('teamId', ''); - // Delete Memberships - $this->deleteByGroup('memberships', [ - Query::equal('teamId', [$teamId]) - ], $this->getProjectDB($project)); - } + // Delete Memberships + deleteByGroup('memberships', [ + Query::equal('teamId', [$teamId]) + ], getProjectDB($project)); +} /** * @param Document $document project document */ - protected function deleteProject(Document $document): void - { - $projectId = $document->getId(); +function deleteProject(Document $document): void +{ + $projectId = $document->getId(); - // Delete all DBs - $this->getProjectDB($document)->delete($projectId); + // Delete all DBs + getProjectDB($document)->delete($projectId); - // Delete all storage directories - $uploads = $this->getFilesDevice($document->getId()); - $cache = new Local(APP_STORAGE_CACHE . '/app-' . $document->getId()); + // Delete all storage directories + $uploads = getFilesDevice($document->getId()); + $cache = new Local(APP_STORAGE_CACHE . '/app-' . $document->getId()); - $uploads->delete($uploads->getRoot(), true); - $cache->delete($cache->getRoot(), true); - } + $uploads->delete($uploads->getRoot(), true); + $cache->delete($cache->getRoot(), true); +} /** * @param Document $document user document * @param Document $project */ - protected function deleteUser(Document $document, Document $project): void - { - $userId = $document->getId(); +function deleteUser(Document $document, Document $project): void +{ + $userId = $document->getId(); - $dbForProject = $this->getProjectDB($project); + $dbForProject = getProjectDB($project); - // Delete all sessions of this user from the sessions table and update the sessions field of the user record - $this->deleteByGroup('sessions', [ - Query::equal('userId', [$userId]) - ], $dbForProject); + // Delete all sessions of this user from the sessions table and update the sessions field of the user record + deleteByGroup('sessions', [ + Query::equal('userId', [$userId]) + ], $dbForProject); - $dbForProject->deleteCachedDocument('users', $userId); + $dbForProject->deleteCachedDocument('users', $userId); - // Delete Memberships and decrement team membership counts - $this->deleteByGroup('memberships', [ - Query::equal('userId', [$userId]) - ], $dbForProject, function (Document $document) use ($dbForProject) { - if ($document->getAttribute('confirm')) { // Count only confirmed members - $teamId = $document->getAttribute('teamId'); - $team = $dbForProject->getDocument('teams', $teamId); - if (!$team->isEmpty()) { - $team = $dbForProject->updateDocument( - 'teams', - $teamId, - // Ensure that total >= 0 - $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)) - ); - } + // Delete Memberships and decrement team membership counts + deleteByGroup('memberships', [ + Query::equal('userId', [$userId]) + ], $dbForProject, function (Document $document) use ($dbForProject) { + if ($document->getAttribute('confirm')) { // Count only confirmed members + $teamId = $document->getAttribute('teamId'); + $team = $dbForProject->getDocument('teams', $teamId); + if (!$team->isEmpty()) { + $team = $dbForProject->updateDocument( + 'teams', + $teamId, + // Ensure that total >= 0 + $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)) + ); } - }); + } + }); - // Delete tokens - $this->deleteByGroup('tokens', [ - Query::equal('userId', [$userId]) + // Delete tokens + deleteByGroup('tokens', [ + Query::equal('userId', [$userId]) + ], $dbForProject); +} + + /** + * @param string $datetime + */ +function deleteExecutionLogs(string $datetime): void +{ + deleteForProjectIds(function (Document $project) use ($datetime) { + $dbForProject = getProjectDB($project); + // Delete Executions + deleteByGroup('executions', [ + Query::lessThan('$createdAt', $datetime) ], $dbForProject); - } + }); +} + +function deleteExpiredSessions(): void +{ + $consoleDB = getConsoleDB(); + + deleteForProjectIds(function (Document $project) use ($consoleDB) { + $dbForProject = getProjectDB($project); + + $project = $consoleDB->getDocument('projects', $project->getId()); + $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); + + // Delete Sessions + deleteByGroup('sessions', [ + Query::lessThan('$createdAt', $expired) + ], $dbForProject); + }); +} /** * @param string $datetime */ - protected function deleteExecutionLogs(string $datetime): void - { - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $dbForProject = $this->getProjectDB($project); - // Delete Executions - $this->deleteByGroup('executions', [ - Query::lessThan('$createdAt', $datetime) - ], $dbForProject); - }); - } - - protected function deleteExpiredSessions(): void - { - $consoleDB = $this->getConsoleDB(); - - $this->deleteForProjectIds(function (Document $project) use ($consoleDB) { - $dbForProject = $this->getProjectDB($project); - - $project = $consoleDB->getDocument('projects', $project->getId()); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); - - // Delete Sessions - $this->deleteByGroup('sessions', [ - Query::lessThan('$createdAt', $expired) - ], $dbForProject); - }); - } - - /** - * @param string $datetime - */ - protected function deleteRealtimeUsage(string $datetime): void - { - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $dbForProject = $this->getProjectDB($project); - // Delete Dead Realtime Logs - $this->deleteByGroup('realtime', [ - Query::lessThan('timestamp', $datetime) - ], $dbForProject); - }); - } +function deleteRealtimeUsage(string $datetime): void +{ + deleteForProjectIds(function (Document $project) use ($datetime) { + $dbForProject = getProjectDB($project); + // Delete Dead Realtime Logs + deleteByGroup('realtime', [ + Query::lessThan('timestamp', $datetime) + ], $dbForProject); + }); +} /** * @param string $datetime * @throws Exception */ - protected function deleteAbuseLogs(string $datetime): void - { - if (empty($datetime)) { - throw new Exception('Failed to delete audit logs. No datetime provided'); - } - - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $timeLimit = new TimeLimit("", 0, 1, $dbForProject); - $abuse = new Abuse($timeLimit); - $status = $abuse->cleanup($datetime); - if (!$status) { - throw new Exception('Failed to delete Abuse logs for project ' . $projectId); - } - }); +function deleteAbuseLogs(string $datetime): void +{ + if (empty($datetime)) { + throw new Exception('Failed to delete audit logs. No datetime provided'); } + deleteForProjectIds(function (Document $project) use ($datetime) { + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $timeLimit = new TimeLimit("", 0, 1, $dbForProject); + $abuse = new Abuse($timeLimit); + $status = $abuse->cleanup($datetime); + if (!$status) { + throw new Exception('Failed to delete Abuse logs for project ' . $projectId); + } + }); +} + /** * @param string $datetime * @throws Exception */ - protected function deleteAuditLogs(string $datetime): void - { - if (empty($datetime)) { - throw new Exception('Failed to delete audit logs. No datetime provided'); - } - - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $audit = new Audit($dbForProject); - $status = $audit->cleanup($datetime); - if (!$status) { - throw new Exception('Failed to delete Audit logs for project' . $projectId); - } - }); +function deleteAuditLogs(string $datetime): void +{ + if (empty($datetime)) { + throw new Exception('Failed to delete audit logs. No datetime provided'); } + deleteForProjectIds(function (Document $project) use ($datetime) { + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $audit = new Audit($dbForProject); + $status = $audit->cleanup($datetime); + if (!$status) { + throw new Exception('Failed to delete Audit logs for project' . $projectId); + } + }); +} + /** * @param string $resource * @param Document $project */ - protected function deleteAuditLogsByResource(string $resource, Document $project): void - { - $dbForProject = $this->getProjectDB($project); +function deleteAuditLogsByResource(string $resource, Document $project): void +{ + $dbForProject = getProjectDB($project); - $this->deleteByGroup(Audit::COLLECTION, [ - Query::equal('resource', [$resource]) - ], $dbForProject); - } + deleteByGroup(Audit::COLLECTION, [ + Query::equal('resource', [$resource]) + ], $dbForProject); +} /** * @param Document $document function document * @param Document $project */ - protected function deleteFunction(Document $document, Document $project): void - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $functionId = $document->getId(); - - /** - * Delete Variables - */ - Console::info("Deleting variables for function " . $functionId); - $this->deleteByGroup('variables', [ - Query::equal('functionId', [$functionId]) - ], $dbForProject); - - /** - * Delete Deployments - */ - Console::info("Deleting deployments for function " . $functionId); - $storageFunctions = $this->getFunctionsDevice($projectId); - $deploymentIds = []; - $this->deleteByGroup('deployments', [ - Query::equal('resourceId', [$functionId]) - ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) { - $deploymentIds[] = $document->getId(); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } - }); - - /** - * Delete builds - */ - Console::info("Deleting builds for function " . $functionId); - $storageBuilds = $this->getBuildsDevice($projectId); - foreach ($deploymentIds as $deploymentId) { - $this->deleteByGroup('builds', [ - Query::equal('deploymentId', [$deploymentId]) - ], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) { - if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); - } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); - } - }); - } - - /** - * Delete Executions - */ - Console::info("Deleting executions for function " . $functionId); - $this->deleteByGroup('executions', [ - Query::equal('functionId', [$functionId]) - ], $dbForProject); - - // TODO: Request executor to delete runtime - } +function deleteFunction(Document $document, Document $project): void +{ + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $functionId = $document->getId(); /** - * @param Document $document deployment document - * @param Document $project + * Delete Variables */ - protected function deleteDeployment(Document $document, Document $project): void - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $deploymentId = $document->getId(); - $functionId = $document->getAttribute('resourceId'); + Console::info("Deleting variables for function " . $functionId); + deleteByGroup('variables', [ + Query::equal('functionId', [$functionId]) + ], $dbForProject); - /** - * Delete deployment files - */ - Console::info("Deleting deployment files for deployment " . $deploymentId); - $storageFunctions = $this->getFunctionsDevice($projectId); + /** + * Delete Deployments + */ + Console::info("Deleting deployments for function " . $functionId); + $storageFunctions = getFunctionsDevice($projectId); + $deploymentIds = []; + deleteByGroup('deployments', [ + Query::equal('resourceId', [$functionId]) + ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) { + $deploymentIds[] = $document->getId(); if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); } else { Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); } + }); - /** - * Delete builds - */ - Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $this->getBuildsDevice($projectId); - $this->deleteByGroup('builds', [ + /** + * Delete builds + */ + Console::info("Deleting builds for function " . $functionId); + $storageBuilds = getBuildsDevice($projectId); + foreach ($deploymentIds as $deploymentId) { + deleteByGroup('builds', [ Query::equal('deploymentId', [$deploymentId]) - ], $dbForProject, function (Document $document) use ($storageBuilds) { + ], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) { if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); } else { Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); } }); - - // TODO: Request executor to delete runtime } + /** + * Delete Executions + */ + Console::info("Deleting executions for function " . $functionId); + deleteByGroup('executions', [ + Query::equal('functionId', [$functionId]) + ], $dbForProject); + + // TODO: Request executor to delete runtime +} + + /** + * @param Document $document deployment document + * @param Document $project + */ +function deleteDeployment(Document $document, Document $project): void +{ + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $deploymentId = $document->getId(); + $functionId = $document->getAttribute('resourceId'); + + /** + * Delete deployment files + */ + Console::info("Deleting deployment files for deployment " . $deploymentId); + $storageFunctions = getFunctionsDevice($projectId); + if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); + } else { + Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); + } + + /** + * Delete builds + */ + Console::info("Deleting builds for deployment " . $deploymentId); + $storageBuilds = getBuildsDevice($projectId); + deleteByGroup('builds', [ + Query::equal('deploymentId', [$deploymentId]) + ], $dbForProject, function (Document $document) use ($storageBuilds) { + if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); + } else { + Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); + } + }); + + // TODO: Request executor to delete runtime +} + /** * @param Document $document to be deleted @@ -553,183 +548,185 @@ class DeletesV1 extends Worker * * @return bool */ - protected function deleteById(Document $document, Database $database, callable $callback = null): bool - { - if ($database->deleteDocument($document->getCollection(), $document->getId())) { - Console::success('Deleted document "' . $document->getId() . '" successfully'); +function deleteById(Document $document, Database $database, callable $callback = null): bool +{ + if ($database->deleteDocument($document->getCollection(), $document->getId())) { + Console::success('Deleted document "' . $document->getId() . '" successfully'); + if (is_callable($callback)) { + $callback($document); + } + + return true; + } else { + Console::error('Failed to delete document: ' . $document->getId()); + return false; + } +} + + /** + * @param callable $callback + */ +function deleteForProjectIds(callable $callback): void +{ + // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document + $count = 0; + $chunk = 0; + $limit = 50; + $projects = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $projects = getConsoleDB()->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); + + $chunk++; + + /** @var string[] $projectIds */ + $sum = count($projects); + + Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects'); + foreach ($projects as $project) { + $callback($project); + $count++; + } + } + + $executionEnd = \microtime(true); + Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); +} + + /** + * @param string $collection collectionID + * @param Query[] $queries + * @param Database $database + * @param callable $callback + */ +function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void +{ + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + + $sum = count($results); + + Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); + + foreach ($results as $document) { + deleteById($document, $database, $callback); + $count++; + } + } + + $executionEnd = \microtime(true); + + Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); +} + + /** + * @param string $collection collectionID + * @param Query[] $queries + * @param Database $database + * @param callable $callback + */ +function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void +{ + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + + $sum = count($results); + + foreach ($results as $document) { if (is_callable($callback)) { $callback($document); } - return true; - } else { - Console::error('Failed to delete document: ' . $document->getId()); - return false; + $count++; } } - /** - * @param callable $callback - */ - protected function deleteForProjectIds(callable $callback): void - { - // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document - $count = 0; - $chunk = 0; - $limit = 50; - $projects = []; - $sum = $limit; + $executionEnd = \microtime(true); - $executionStart = \microtime(true); - - while ($sum === $limit) { - $projects = $this->getConsoleDB()->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); - - $chunk++; - - /** @var string[] $projectIds */ - $sum = count($projects); - - Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects'); - foreach ($projects as $project) { - $callback($project); - $count++; - } - } - - $executionEnd = \microtime(true); - Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); - } - - /** - * @param string $collection collectionID - * @param Query[] $queries - * @param Database $database - * @param callable $callback - */ - protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void - { - $count = 0; - $chunk = 0; - $limit = 50; - $results = []; - $sum = $limit; - - $executionStart = \microtime(true); - - while ($sum === $limit) { - $chunk++; - - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); - - $sum = count($results); - - Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); - - foreach ($results as $document) { - $this->deleteById($document, $database, $callback); - $count++; - } - } - - $executionEnd = \microtime(true); - - Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); - } - - /** - * @param string $collection collectionID - * @param Query[] $queries - * @param Database $database - * @param callable $callback - */ - protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void - { - $count = 0; - $chunk = 0; - $limit = 50; - $results = []; - $sum = $limit; - - $executionStart = \microtime(true); - - while ($sum === $limit) { - $chunk++; - - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); - - $sum = count($results); - - foreach ($results as $document) { - if (is_callable($callback)) { - $callback($document); - } - - $count++; - } - } - - $executionEnd = \microtime(true); - - Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); - } + Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); +} /** * @param Document $document certificates document */ - protected function deleteCertificates(Document $document): void - { - $consoleDB = $this->getConsoleDB(); +function deleteCertificates(Document $document): void +{ + $consoleDB = getConsoleDB(); - // If domain has certificate generated - if (isset($document['certificateId'])) { - $domainUsingCertificate = $consoleDB->findOne('domains', [ - Query::equal('certificateId', [$document['certificateId']]) - ]); + // If domain has certificate generated + if (isset($document['certificateId'])) { + $domainUsingCertificate = $consoleDB->findOne('domains', [ + Query::equal('certificateId', [$document['certificateId']]) + ]); - if (!$domainUsingCertificate) { - $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); - if ($mainDomain === $document->getAttribute('domain')) { - $domainUsingCertificate = $mainDomain; - } - } - - // If certificate is still used by some domain, mark we can't delete. - // Current domain should not be found, because we only have copy. Original domain is already deleted from database. - if ($domainUsingCertificate) { - Console::warning("Skipping certificate deletion, because a domain is still using it."); - return; + if (!$domainUsingCertificate) { + $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); + if ($mainDomain === $document->getAttribute('domain')) { + $domainUsingCertificate = $mainDomain; } } - $domain = $document->getAttribute('domain'); - $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; - $checkTraversal = realpath($directory) === $directory; - - if ($domain && $checkTraversal && is_dir($directory)) { - // Delete certificate document, so Appwrite is aware of change - if (isset($document['certificateId'])) { - $consoleDB->deleteDocument('certificates', $document['certificateId']); - } - - // Delete files, so Traefik is aware of change - array_map('unlink', glob($directory . '/*.*')); - rmdir($directory); - Console::info("Deleted certificate files for {$domain}"); - } else { - Console::info("No certificate files found for {$domain}"); + // If certificate is still used by some domain, mark we can't delete. + // Current domain should not be found, because we only have copy. Original domain is already deleted from database. + if ($domainUsingCertificate) { + Console::warning("Skipping certificate deletion, because a domain is still using it."); + return; } } - protected function deleteBucket(Document $document, Document $project) - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); + $domain = $document->getAttribute('domain'); + $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; + $checkTraversal = realpath($directory) === $directory; - $device = $this->getFilesDevice($projectId); + if ($domain && $checkTraversal && is_dir($directory)) { + // Delete certificate document, so Appwrite is aware of change + if (isset($document['certificateId'])) { + $consoleDB->deleteDocument('certificates', $document['certificateId']); + } - $device->deletePath($document->getId()); + // Delete files, so Traefik is aware of change + array_map('unlink', glob($directory . '/*.*')); + rmdir($directory); + Console::info("Deleted certificate files for {$domain}"); + } else { + Console::info("No certificate files found for {$domain}"); } } + +function deleteBucket(Document $document, Document $project) +{ + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); + + $device = getFilesDevice($projectId); + + $device->deletePath($document->getId()); +} + +$server->workerStart(); +$server->start(); diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index 9be6e27b33..08fd3cad3f 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Resque; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -15,7 +14,7 @@ class Audit extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME); + parent::__construct(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME, $connection); } /** diff --git a/src/Appwrite/Event/Build.php b/src/Appwrite/Event/Build.php index 4d4b338118..1009dcedf4 100644 --- a/src/Appwrite/Event/Build.php +++ b/src/Appwrite/Event/Build.php @@ -2,8 +2,9 @@ namespace Appwrite\Event; -use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Build extends Event { @@ -11,9 +12,9 @@ class Build extends Event protected ?Document $resource = null; protected ?Document $deployment = null; - public function __construct() + public function __construct(protected Connection $connection) { - parent::__construct(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME); + parent::__construct(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, $connection); } /** @@ -93,7 +94,11 @@ class Build extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; + + return $client->enqueue([ 'project' => $this->project, 'resource' => $this->resource, 'deployment' => $this->deployment, diff --git a/src/Appwrite/Event/Certificate.php b/src/Appwrite/Event/Certificate.php index 0f0cbaa73d..1a755b7332 100644 --- a/src/Appwrite/Event/Certificate.php +++ b/src/Appwrite/Event/Certificate.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Resque; use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -14,7 +13,7 @@ class Certificate extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME); + parent::__construct(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, $connection); } /** diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 2c6aaea926..7d95b347cd 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Resque; use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -16,7 +15,7 @@ class Database extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME); + parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME, $connection); } /** diff --git a/src/Appwrite/Event/Delete.php b/src/Appwrite/Event/Delete.php index ad69172062..32c84ee90c 100644 --- a/src/Appwrite/Event/Delete.php +++ b/src/Appwrite/Event/Delete.php @@ -2,8 +2,8 @@ namespace Appwrite\Event; -use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; use Utopia\Queue\Connection; class Delete extends Event @@ -122,7 +122,11 @@ class Delete extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + $client = new Client($this->queue, $this->connection); + + $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; + + return $client->enqueue([ 'project' => $this->project, 'type' => $this->type, 'document' => $this->document, diff --git a/src/Appwrite/Event/Mail.php b/src/Appwrite/Event/Mail.php index cffee488f7..7c1a7b53b4 100644 --- a/src/Appwrite/Event/Mail.php +++ b/src/Appwrite/Event/Mail.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Resque; use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -18,7 +17,7 @@ class Mail extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME); + parent::__construct(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME, $connection); } /** diff --git a/src/Appwrite/Event/Phone.php b/src/Appwrite/Event/Phone.php index 0399e27e45..c18b861e24 100644 --- a/src/Appwrite/Event/Phone.php +++ b/src/Appwrite/Event/Phone.php @@ -12,7 +12,7 @@ class Phone extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME); + parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME, $connection); } /** From b0c8e8980db6dac18e2bc8219cd97d5c4063ff42 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 12:48:55 +0000 Subject: [PATCH 015/144] Continue removing resque --- app/controllers/api/health.php | 17 +++++++++++------ app/workers/builds.php | 1 - app/workers/deletes.php | 1 - app/workers/mails.php | 1 - app/workers/webhooks.php | 1 - src/Appwrite/Event/Event.php | 12 ++++++++++++ tests/unit/Event/EventTest.php | 6 +----- 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index d6a2612f32..4c40953f9b 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -1,6 +1,8 @@ label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('events') ->inject('response') - ->action(function (Response $response) { + ->action(function (Event $events, Response $response) { - $response->dynamic(new Document([ 'size' => Resque::size(Event::WEBHOOK_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $events->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/logs') @@ -364,10 +367,11 @@ App::get('/v1/health/queue/logs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('audits') ->inject('response') - ->action(function (Response $response) { + ->action(function (Audit $audits, Response $response) { - $response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $audits->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/certificates') @@ -381,10 +385,11 @@ App::get('/v1/health/queue/certificates') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('certificates') ->inject('response') - ->action(function (Response $response) { + ->action(function (Certificate $certificates, Response $response) { - $response->dynamic(new Document([ 'size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $certificates->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/functions') diff --git a/app/workers/builds.php b/app/workers/builds.php index 9ca077dfcd..63ecf6af2f 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -5,7 +5,6 @@ require_once __DIR__ . '/../worker.php'; use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Messaging\Adapter\Realtime; -use Appwrite\Resque\Worker; use Appwrite\Utopia\Response\Model\Deployment; use Executor\Executor; use Appwrite\Usage\Stats; diff --git a/app/workers/deletes.php b/app/workers/deletes.php index e546c314b9..835cc78e80 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -9,7 +9,6 @@ use Utopia\Cache\Cache; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Appwrite\Resque\Worker; use Executor\Executor; use Utopia\Storage\Device\Local; use Utopia\Abuse\Abuse; diff --git a/app/workers/mails.php b/app/workers/mails.php index 7d2927d562..f0658fedca 100644 --- a/app/workers/mails.php +++ b/app/workers/mails.php @@ -1,6 +1,5 @@ queue, $this->connection); + + return $client->getQueueSize(); + } + /** * Resets event. * diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index 6a001fac1e..a276733770 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -17,10 +17,6 @@ class EventTest extends TestCase public function setUp(): void { - $redisHost = App::getEnv('_APP_REDIS_HOST', ''); - $redisPort = App::getEnv('_APP_REDIS_PORT', ''); - \Resque::setBackend($redisHost . ':' . $redisPort); - $fallbackForRedis = URL::unparse([ 'scheme' => 'redis', 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), @@ -74,7 +70,7 @@ class EventTest extends TestCase $this->assertEquals('eventValue1', $this->object->getParam('eventKey1')); $this->assertEquals('eventValue2', $this->object->getParam('eventKey2')); $this->assertEquals(null, $this->object->getParam('eventKey3')); - $this->assertEquals(\Resque::size($this->queue), 1); + $this->assertEquals($this->object->getQueueSize(), 1); } public function testReset(): void From e577c04fae3f128416dcca1fced78fd15c7d9b04 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 12:49:33 +0000 Subject: [PATCH 016/144] Update Maintenance.php --- src/Appwrite/Platform/Tasks/Maintenance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index a27c650ff1..7cba04315e 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -26,7 +26,7 @@ class Maintenance extends Action public function __construct() { $this - ->desc('Schedules maintenance tasks and publishes them to resque') + ->desc('Schedules maintenance tasks and publishes them to queues') ->inject('dbForConsole') ->inject('certificates') ->inject('deletes') From 740bae6a9978ba0ebc2ace1715bf261ff05d40fe Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 13:01:39 +0000 Subject: [PATCH 017/144] Update console --- app/console | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/console b/app/console index 43891a526e..c1ac63889c 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 43891a526e061454617cbb13def3c4901d99a7f1 +Subproject commit c1ac63889c0a97b280599355e526669bb207880a From 1cd84aacc7ceaca23679000ebd367524cf041ba1 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 13:06:57 +0000 Subject: [PATCH 018/144] Update Event.php --- src/Appwrite/Event/Event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 127639b6d0..0550063497 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -278,7 +278,7 @@ class Event /** * Get Queue Size - * + * * @return int */ public function getQueueSize(): int From 0b17a737febd96817744db6f8b0019016302888d Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 15:42:04 +0000 Subject: [PATCH 019/144] Fix Functions Worker --- app/workers/functions.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/workers/functions.php b/app/workers/functions.php index 51af6ec780..5ae5f852d2 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -28,18 +28,18 @@ Authorization::setDefaultStatus(false); Server::setResource('execute', function () { return function ( - Func $queueForFunctions, - Database $dbForProject, Client $statsd, + Database $dbForProject, Document $project, Document $function, + Func $queueForFunctions, string $trigger, - string $data = null, - ?Document $user = null, - string $jwt = null, string $event = null, string $eventData = null, + ?Document $user = null, + string $data = null, string $executionId = null, + string $jwt = null, Connection $queueConnection, ) { $user ??= new Document(); @@ -163,8 +163,6 @@ Server::setResource('execute', function () { $execution = $dbForProject->updateDocument('executions', $executionId, $execution); - $connection = $ - /** Trigger Webhook */ $executionModel = new Execution(); $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $queueConnection); @@ -315,6 +313,7 @@ $server->job() user: $user, jwt: $jwt, statsd: $statsd, + queueConnection: $queue ); break; case 'schedule': @@ -331,6 +330,7 @@ $server->job() user: null, jwt: null, statsd: $statsd, + queueConnection: $queue ); break; } From c0daed36443b4ee6a7af68002dde2b7aad91e0c8 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 20 Dec 2022 16:11:30 +0000 Subject: [PATCH 020/144] Refactor names and cleanup code --- app/cli.php | 19 +-- app/controllers/api/account.php | 160 +++++++++--------- app/controllers/api/databases.php | 172 ++++++++++---------- app/controllers/api/functions.php | 64 ++++---- app/controllers/api/health.php | 18 +- app/controllers/api/projects.php | 14 +- app/controllers/api/storage.php | 48 +++--- app/controllers/api/teams.php | 52 +++--- app/controllers/api/users.php | 128 +++++++-------- app/controllers/general.php | 2 +- app/controllers/shared/api.php | 80 ++++----- app/init.php | 19 +-- app/worker.php | 52 +----- src/Appwrite/Event/Func.php | 4 +- src/Appwrite/Platform/Tasks/Maintenance.php | 60 +++---- 15 files changed, 426 insertions(+), 466 deletions(-) diff --git a/app/cli.php b/app/cli.php index 4106ae35b5..b327acce28 100644 --- a/app/cli.php +++ b/app/cli.php @@ -153,34 +153,31 @@ CLI::setResource('influxdb', function (Registry $register) { CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); -CLI::setResource('messaging', function (Connection $queue) { +CLI::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); -CLI::setResource('mails', function (Connection $queue) { +CLI::setResource('queueForMail', function (Connection $queue) { return new Mail($queue); }, ['queue']); -CLI::setResource('builds', function (Connection $queue) { +CLI::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); -CLI::setResource('database', function (Connection $queue) { +CLI::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); -CLI::setResource('deletes', function (Connection $queue) { +CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); -CLI::setResource('events', function (Connection $queue) { +CLI::setResource('queueForEvents', function (Connection $queue) { return new Event('', '', $queue); }, ['queue']); -CLI::setResource('audits', function (Connection $queue) { +CLI::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); }, ['queue']); -CLI::setResource('events', function (Connection $queue) { - return new Event('', '', $queue); -}, ['queue']); CLI::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -CLI::setResource('certificates', function (Connection $queue) { +CLI::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); CLI::setResource('logError', function (Registry $register) { diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 3d6e7bf762..2680b530b9 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -70,8 +70,8 @@ App::post('/v1/account') ->inject('response') ->inject('project') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $project, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { $email = \strtolower($email); if ('console' === $project->getId()) { @@ -130,7 +130,7 @@ App::post('/v1/account') Authorization::setRole(Role::user($user->getId())->toString()); Authorization::setRole(Role::users()->toString()); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -166,8 +166,8 @@ App::post('/v1/account/sessions/email') ->inject('project') ->inject('locale') ->inject('geodb') - ->inject('events') - ->action(function (string $email, string $password, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $events) { + ->inject('queueForEvents') + ->action(function (string $email, string $password, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -247,7 +247,7 @@ App::post('/v1/account/sessions/email') ->setAttribute('expire', $expire) ; - $events + $queueForEvents ->setParam('userId', $profile->getId()) ->setParam('sessionId', $session->getId()) ; @@ -386,8 +386,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('user') ->inject('dbForProject') ->inject('geodb') - ->inject('events') - ->action(function (string $provider, string $code, string $state, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $events) use ($oauthDefaultSuccess) { + ->inject('queueForEvents') + ->action(function (string $provider, string $code, string $state, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); @@ -574,7 +574,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $session->setAttribute('expire', $expire); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) ->setPayload($response->output($session, Response::MODEL_SESSION)) @@ -630,9 +630,9 @@ App::post('/v1/account/sessions/magic-url') ->inject('project') ->inject('dbForProject') ->inject('locale') - ->inject('events') - ->inject('mails') - ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Event $events, Mail $mails) { + ->inject('queueForEvents') + ->inject('queueForMail') + ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMail) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -714,7 +714,7 @@ App::post('/v1/account/sessions/magic-url') $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $loginSecret, 'expire' => $expire, 'project' => $project->getId()]); $url = Template::unParseURL($url); - $mails + $queueForMail ->setType(MAIL_TYPE_MAGIC_SESSION) ->setRecipient($user->getAttribute('email')) ->setUrl($url) @@ -722,7 +722,7 @@ App::post('/v1/account/sessions/magic-url') ->trigger() ; - $events->setPayload( + $queueForEvents->setPayload( $response->output( $token->setAttribute('secret', $loginSecret), Response::MODEL_TOKEN @@ -765,8 +765,8 @@ App::put('/v1/account/sessions/magic-url') ->inject('project') ->inject('locale') ->inject('geodb') - ->inject('events') - ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { /** @var Utopia\Database\Document $user */ @@ -832,7 +832,7 @@ App::put('/v1/account/sessions/magic-url') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) ; @@ -883,9 +883,9 @@ App::post('/v1/account/sessions/phone') ->inject('response') ->inject('project') ->inject('dbForProject') - ->inject('events') - ->inject('messaging') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $project, Database $dbForProject, Event $events, EventPhone $messaging) { + ->inject('queueForEvents') + ->inject('queueForMessaging') + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $project, Database $dbForProject, Event $queueForEvents, EventPhone $queueForMessaging) { if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); @@ -959,12 +959,12 @@ App::post('/v1/account/sessions/phone') $dbForProject->deleteCachedDocument('users', $user->getId()); - $messaging + $queueForMessaging ->setRecipient($phone) ->setMessage($secret) ->trigger(); - $events->setPayload( + $queueForEvents->setPayload( $response->output( $token->setAttribute('secret', $secret), Response::MODEL_TOKEN @@ -1004,8 +1004,8 @@ App::put('/v1/account/sessions/phone') ->inject('project') ->inject('locale') ->inject('geodb') - ->inject('events') - ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { $user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -1067,7 +1067,7 @@ App::put('/v1/account/sessions/phone') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) ; @@ -1122,8 +1122,8 @@ App::post('/v1/account/sessions/anonymous') ->inject('project') ->inject('dbForProject') ->inject('geodb') - ->inject('events') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $events) { + ->inject('queueForEvents') + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { $protocol = $request->getProtocol(); @@ -1203,7 +1203,7 @@ App::post('/v1/account/sessions/anonymous') $dbForProject->deleteCachedDocument('users', $user->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) ; @@ -1475,14 +1475,14 @@ App::patch('/v1/account/name') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $name, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $name, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->updateDocument('users', $user->getId(), $user ->setAttribute('name', $name) ->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email', ''), $user->getAttribute('phone', '')]))); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_ACCOUNT); }); @@ -1508,8 +1508,8 @@ App::patch('/v1/account/password') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { // Check old password only if its an existing user. if (!empty($user->getAttribute('passwordUpdate')) && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password @@ -1522,7 +1522,7 @@ App::patch('/v1/account/password') ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) ->setAttribute('passwordUpdate', DateTime::now())); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_ACCOUNT); }); @@ -1547,8 +1547,8 @@ App::patch('/v1/account/email') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $email, string $password, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $email, string $password, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $isAnonymousUser = Auth::isAnonymousUser($user); // Check if request is from an anonymous account for converting if ( @@ -1574,7 +1574,7 @@ App::patch('/v1/account/email') throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_ACCOUNT); }); @@ -1599,8 +1599,8 @@ App::patch('/v1/account/phone') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $isAnonymousUser = Auth::isAnonymousUser($user); // Check if request is from an anonymous account for converting @@ -1622,7 +1622,7 @@ App::patch('/v1/account/phone') throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS); } - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_ACCOUNT); }); @@ -1646,12 +1646,12 @@ App::patch('/v1/account/prefs') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (array $prefs, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (array $prefs, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_ACCOUNT); }); @@ -1675,12 +1675,12 @@ App::patch('/v1/account/status') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', false)); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setPayload($response->output($user, Response::MODEL_ACCOUNT)); @@ -1712,9 +1712,9 @@ App::delete('/v1/account/sessions/:sessionId') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('events') + ->inject('queueForEvents') ->inject('project') - ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $events, Document $project) { + ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Document $project) { $protocol = $request->getProtocol(); $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; @@ -1752,7 +1752,7 @@ App::delete('/v1/account/sessions/:sessionId') $dbForProject->deleteCachedDocument('users', $user->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) ->setPayload($response->output($session, Response::MODEL_SESSION)) @@ -1788,8 +1788,8 @@ App::patch('/v1/account/sessions/:sessionId') ->inject('dbForProject') ->inject('project') ->inject('locale') - ->inject('events') - ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Event $events) { + ->inject('queueForEvents') + ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Event $queueForEvents) { $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; $sessionId = ($sessionId === 'current') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration) @@ -1837,7 +1837,7 @@ App::patch('/v1/account/sessions/:sessionId') $session->setAttribute('expire', DateTime::addSeconds(new \DateTime($session->getCreatedAt()), $authDuration)); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) ->setPayload($response->output($session, Response::MODEL_SESSION)) @@ -1870,8 +1870,8 @@ App::delete('/v1/account/sessions') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('events') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $events) { + ->inject('queueForEvents') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents) { $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); @@ -1898,13 +1898,13 @@ App::delete('/v1/account/sessions') ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); // Use current session for events. - $events->setPayload($response->output($session, Response::MODEL_SESSION)); + $queueForEvents->setPayload($response->output($session, Response::MODEL_SESSION)); } } $dbForProject->deleteCachedDocument('users', $user->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()); @@ -1936,9 +1936,9 @@ App::post('/v1/account/recovery') ->inject('dbForProject') ->inject('project') ->inject('locale') - ->inject('mails') - ->inject('events') - ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Event $events) { + ->inject('queueForMail') + ->inject('queueForEvents') + ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMail, Event $queueForEvents) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -1991,7 +1991,7 @@ App::post('/v1/account/recovery') $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]); $url = Template::unParseURL($url); - $mails + $queueForMail ->setType(MAIL_TYPE_RECOVERY) ->setRecipient($profile->getAttribute('email', '')) ->setUrl($url) @@ -2000,7 +2000,7 @@ App::post('/v1/account/recovery') ->trigger(); ; - $events + $queueForEvents ->setParam('userId', $profile->getId()) ->setParam('tokenId', $recovery->getId()) ->setUser($profile) @@ -2042,8 +2042,8 @@ App::put('/v1/account/recovery') ->param('passwordAgain', '', new Password(), 'Repeat new user password. Must be at least 8 chars.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Event $queueForEvents) { if ($password !== $passwordAgain) { throw new Exception(Exception::USER_PASSWORD_MISMATCH); } @@ -2079,7 +2079,7 @@ App::put('/v1/account/recovery') $dbForProject->deleteDocument('tokens', $recovery); $dbForProject->deleteCachedDocument('users', $profile->getId()); - $events + $queueForEvents ->setParam('userId', $profile->getId()) ->setParam('tokenId', $recoveryDocument->getId()) ; @@ -2111,9 +2111,9 @@ App::post('/v1/account/verification') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('events') - ->inject('mails') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $events, Mail $mails) { + ->inject('queueForEvents') + ->inject('queueForMail') + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMail) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -2151,7 +2151,7 @@ App::post('/v1/account/verification') $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]); $url = Template::unParseURL($url); - $mails + $queueForMail ->setType(MAIL_TYPE_VERIFICATION) ->setRecipient($user->getAttribute('email')) ->setUrl($url) @@ -2160,7 +2160,7 @@ App::post('/v1/account/verification') ->trigger() ; - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('tokenId', $verification->getId()) ->setPayload($response->output( @@ -2199,8 +2199,8 @@ App::put('/v1/account/verification') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -2228,7 +2228,7 @@ App::put('/v1/account/verification') $dbForProject->deleteDocument('tokens', $verification); $dbForProject->deleteCachedDocument('users', $profile->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('tokenId', $verificationDocument->getId()) ; @@ -2257,9 +2257,9 @@ App::post('/v1/account/verification/phone') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->inject('messaging') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $events, EventPhone $messaging) { + ->inject('queueForEvents') + ->inject('queueForMessaging') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, EventPhone $queueForMessaging) { if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED); @@ -2298,13 +2298,13 @@ App::post('/v1/account/verification/phone') $dbForProject->deleteCachedDocument('users', $user->getId()); - $messaging + $queueForMessaging ->setRecipient($user->getAttribute('phone')) ->setMessage($secret) ->trigger() ; - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('tokenId', $verification->getId()) ->setPayload($response->output( @@ -2343,8 +2343,8 @@ App::put('/v1/account/verification/phone') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -2370,7 +2370,7 @@ App::put('/v1/account/verification/phone') $dbForProject->deleteDocument('tokens', $verification); $dbForProject->deleteCachedDocument('users', $profile->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('tokenId', $verificationDocument->getId()) ; diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 5022681d87..d7465f6730 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -55,7 +55,7 @@ use MaxMind\Db\Reader; * @return Document Newly created attribute document * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $database, Event $events): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $database, Event $queueForEvents): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -134,7 +134,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att ->setDocument($attribute) ; - $events + $queueForEvents ->setContext('collection', $collection) ->setContext('database', $db) ->setParam('databaseId', $databaseId) @@ -166,8 +166,8 @@ App::post('/v1/databases') ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; @@ -215,7 +215,7 @@ App::post('/v1/databases') throw new Exception(Exception::DATABASE_ALREADY_EXISTS); } - $events->setParam('databaseId', $database->getId()); + $queueForEvents->setParam('databaseId', $database->getId()); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -400,8 +400,8 @@ App::put('/v1/databases/:databaseId') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -419,7 +419,7 @@ App::put('/v1/databases/:databaseId') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } - $events->setParam('databaseId', $database->getId()); + $queueForEvents->setParam('databaseId', $database->getId()); $response->dynamic($database, Response::MODEL_DATABASE); }); @@ -441,9 +441,9 @@ App::delete('/v1/databases/:databaseId') ->param('databaseId', '', new UID(), 'Database ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->inject('deletes') - ->action(function (string $databaseId, Response $response, Database $dbForProject, Event $events, Delete $deletes) { + ->inject('queueForEvents') + ->inject('queueForDeletes') + ->action(function (string $databaseId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -457,12 +457,12 @@ App::delete('/v1/databases/:databaseId') $dbForProject->deleteCachedCollection('databases' . $database->getInternalId()); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($database) ; - $events + $queueForEvents ->setParam('databaseId', $database->getId()) ->setPayload($response->output($database, Response::MODEL_DATABASE)) ; @@ -494,8 +494,8 @@ App::post('/v1/databases/:databaseId/collections') ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](/docs/permissions).', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, Response $response, Database $dbForProject, Event $queueForEvents) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -528,7 +528,7 @@ App::post('/v1/databases/:databaseId/collections') throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); } - $events + $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()); @@ -750,8 +750,8 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->param('enabled', true, new Boolean(), 'Is collection enabled?', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -785,7 +785,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } - $events + $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()); @@ -813,9 +813,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->inject('deletes') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, Event $events, Delete $deletes) { + ->inject('queueForEvents') + ->inject('queueForDeletes') + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -835,12 +835,12 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($collection) ; - $events + $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) @@ -876,9 +876,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within required size $validator = new Text($size); @@ -893,7 +893,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -925,9 +925,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -937,7 +937,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -970,9 +970,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // use length of longest string as attribute size $size = 0; @@ -997,7 +997,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_ENUM, 'formatOptions' => ['elements' => $elements], - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1029,9 +1029,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1041,7 +1041,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1073,9 +1073,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1085,7 +1085,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1119,9 +1119,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within range $min = (is_null($min)) ? PHP_INT_MIN : \intval($min); @@ -1151,7 +1151,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1192,9 +1192,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within range $min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min); @@ -1227,7 +1227,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1266,9 +1266,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1277,7 +1277,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1310,9 +1310,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1322,7 +1322,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti 'default' => $default, 'array' => $array, 'filters' => ['datetime'] - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1461,9 +1461,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1490,7 +1490,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); $dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); - $database + $queueForDatabase ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) ->setCollection($collection) ->setDatabase($db) @@ -1516,7 +1516,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key default => Response::MODEL_ATTRIBUTE, }; - $events + $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) ->setParam('attributeId', $attribute->getId()) @@ -1553,9 +1553,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1658,14 +1658,14 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); - $database + $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) ->setDatabase($db) ->setCollection($collection) ->setDocument($index) ; - $events + $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) ->setParam('indexId', $index->getId()) @@ -1786,9 +1786,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->inject('database') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events) { + ->inject('queueForDatabase') + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1814,14 +1814,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); - $database + $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) ->setDatabase($db) ->setCollection($collection) ->setDocument($index) ; - $events + $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) ->setParam('indexId', $index->getId()) @@ -1861,9 +1861,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('response') ->inject('dbForProject') ->inject('user') - ->inject('events') + ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $events, string $mode) { + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -1948,7 +1948,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } - $events + $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) ->setParam('documentId', $document->getId()) @@ -2248,9 +2248,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->inject('response') ->inject('dbForProject') - ->inject('events') + ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Event $events, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Event $queueForEvents, string $mode) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -2344,7 +2344,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } - $events + $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) ->setParam('documentId', $document->getId()) @@ -2379,10 +2379,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->param('documentId', '', new UID(), 'Document ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->inject('deletes') + ->inject('queueForEvents') + ->inject('queueForDeletes') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, Response $response, Database $dbForProject, Event $events, Delete $deletes, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2430,12 +2430,12 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $document->setAttribute('$collectionId', $collectionId); $document->setAttribute('$databaseId', $databaseId); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_AUDIT) ->setDocument($document) ; - $events + $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) ->setParam('documentId', $document->getId()) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 17ca300de1..d44c7082fe 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -70,9 +70,9 @@ App::post('/v1/functions') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') + ->inject('queueForEvents') ->inject('dbForConsole') - ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) { + ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Database $dbForConsole) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $function = $dbForProject->createDocument('functions', new Document([ @@ -104,7 +104,7 @@ App::post('/v1/functions') $function->setAttribute('scheduleId', $schedule->getId()); $dbForProject->updateDocument('functions', $function->getId(), $function); - $eventsInstance->setParam('functionId', $function->getId()); + $queueForEvents->setParam('functionId', $function->getId()); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -446,9 +446,9 @@ App::put('/v1/functions/:functionId') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') + ->inject('queueForEvents') ->inject('dbForConsole') - ->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) { + ->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -485,7 +485,7 @@ App::put('/v1/functions/:functionId') Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - $eventsInstance->setParam('functionId', $function->getId()); + $queueForEvents->setParam('functionId', $function->getId()); $response->dynamic($function, Response::MODEL_FUNCTION); }); @@ -509,9 +509,9 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') ->inject('response') ->inject('dbForProject') ->inject('project') - ->inject('events') + ->inject('queueForEvents') ->inject('dbForConsole') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $events, Database $dbForConsole) { + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -549,7 +549,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - $events + $queueForEvents ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()); @@ -572,11 +572,11 @@ App::delete('/v1/functions/:functionId') ->param('functionId', '', new UID(), 'Function ID.') ->inject('response') ->inject('dbForProject') - ->inject('deletes') - ->inject('events') + ->inject('queueForDeletes') + ->inject('queueForEvents') ->inject('project') ->inject('dbForConsole') - ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $deletes, Event $events, Document $project, Database $dbForConsole) { + ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Document $project, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -597,11 +597,11 @@ App::delete('/v1/functions/:functionId') Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($function); - $events->setParam('functionId', $function->getId()); + $queueForEvents->setParam('functionId', $function->getId()); $response->noContent(); }); @@ -629,13 +629,13 @@ App::post('/v1/functions/:functionId/deployments') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('events') + ->inject('queueForEvents') ->inject('project') ->inject('deviceFunctions') ->inject('deviceLocal') ->inject('dbForConsole') - ->inject('builds') - ->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole, Build $builds) { + ->inject('queueForBuilds') + ->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole, Build $queueForBuilds) { $function = $dbForProject->getDocument('functions', $functionId); @@ -755,7 +755,7 @@ App::post('/v1/functions/:functionId/deployments') } // Start the build - $builds + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) ->setDeployment($deployment) @@ -804,7 +804,7 @@ App::post('/v1/functions/:functionId/deployments') $metadata = null; - $events + $queueForEvents ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()); @@ -939,10 +939,10 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') ->param('deploymentId', '', new UID(), 'Deployment ID.') ->inject('response') ->inject('dbForProject') - ->inject('deletes') - ->inject('events') + ->inject('queueForDeletes') + ->inject('queueForEvents') ->inject('deviceFunctions') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Delete $deletes, Event $events, Device $deviceFunctions) { + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Device $deviceFunctions) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -970,11 +970,11 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') ]))); } - $events + $queueForEvents ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($deployment); @@ -1000,9 +1000,9 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->inject('response') ->inject('dbForProject') ->inject('project') - ->inject('events') - ->inject('builds') - ->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events, Build $builds) { + ->inject('queueForEvents') + ->inject('queueForBuilds') + ->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -1025,12 +1025,12 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') throw new Exception(Exception::BUILD_IN_PROGRESS, 'Build not failed'); } - $events + $queueForEvents ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()); // Retry the build - $builds + $queueForBuilds ->setType(BUILD_TYPE_RETRY) ->setResource($function) ->setDeployment($deployment) @@ -1062,11 +1062,11 @@ App::post('/v1/functions/:functionId/executions') ->inject('project') ->inject('dbForProject') ->inject('user') - ->inject('events') + ->inject('queueForEvents') ->inject('usage') ->inject('mode') ->inject('queueForFunctions') - ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, Stats $usage, string $mode, Func $queueForFunctions) { + ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Stats $usage, string $mode, Func $queueForFunctions) { $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); @@ -1148,7 +1148,7 @@ App::post('/v1/functions/:functionId/executions') } } - $events + $queueForEvents ->setParam('functionId', $function->getId()) ->setParam('executionId', $execution->getId()) ->setContext('function', $function); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 4c40953f9b..0110635052 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -349,11 +349,11 @@ App::get('/v1/health/queue/webhooks') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('events') + ->inject('queueForEvents') ->inject('response') - ->action(function (Event $events, Response $response) { + ->action(function (Event $queueForEvents, Response $response) { - $response->dynamic(new Document([ 'size' => $events->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $queueForEvents->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/logs') @@ -367,11 +367,11 @@ App::get('/v1/health/queue/logs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('audits') + ->inject('queueForAudits') ->inject('response') - ->action(function (Audit $audits, Response $response) { + ->action(function (Audit $queueForAudits, Response $response) { - $response->dynamic(new Document([ 'size' => $audits->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $queueForAudits->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/certificates') @@ -385,11 +385,11 @@ App::get('/v1/health/queue/certificates') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('certificates') + ->inject('queueForCertificates') ->inject('response') - ->action(function (Certificate $certificates, Response $response) { + ->action(function (Certificate $queueForCertificates, Response $response) { - $response->dynamic(new Document([ 'size' => $certificates->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $queueForCertificates->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/functions') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index d307c41dbc..4c4e84467c 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -483,8 +483,8 @@ App::delete('/v1/projects/:projectId') ->inject('response') ->inject('user') ->inject('dbForConsole') - ->inject('deletes') - ->action(function (string $projectId, string $password, Response $response, Document $user, Database $dbForConsole, Delete $deletes) { + ->inject('queueForDeletes') + ->action(function (string $projectId, string $password, Response $response, Document $user, Database $dbForConsole, Delete $queueForDeletes) { if (!Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password throw new Exception(Exception::USER_INVALID_CREDENTIALS); @@ -496,7 +496,7 @@ App::delete('/v1/projects/:projectId') throw new Exception(Exception::PROJECT_NOT_FOUND); } - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($project) ; @@ -1314,7 +1314,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') - ->inject('certificates') + ->inject('queueForCertificates') ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Certificate $queueForCertificates) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1373,8 +1373,8 @@ App::delete('/v1/projects/:projectId/domains/:domainId') ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') - ->inject('deletes') - ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Delete $deletes) { + ->inject('queueForDeletes') + ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Delete $queueForDeletes) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1395,7 +1395,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') $dbForConsole->deleteCachedDocument('projects', $project->getId()); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_CERTIFICATES) ->setDocument($domain); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 71979cca55..ddad15d520 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -71,8 +71,8 @@ App::post('/v1/storage/buckets') ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $queueForEvents) { $bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId; @@ -134,7 +134,7 @@ App::post('/v1/storage/buckets') throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS); } - $events + $queueForEvents ->setParam('bucketId', $bucket->getId()) ; @@ -243,8 +243,8 @@ App::put('/v1/storage/buckets/:bucketId') ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $bucketId, string $name, ?array $permissions, bool $fileSecurity, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, string $compression, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $queueForEvents) { $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -276,7 +276,7 @@ App::put('/v1/storage/buckets/:bucketId') ->setAttribute('compression', $compression) ->setAttribute('antivirus', $antivirus)); - $events + $queueForEvents ->setParam('bucketId', $bucket->getId()) ; @@ -300,9 +300,9 @@ App::delete('/v1/storage/buckets/:bucketId') ->param('bucketId', '', new UID(), 'Bucket unique ID.') ->inject('response') ->inject('dbForProject') - ->inject('deletes') - ->inject('events') - ->action(function (string $bucketId, Response $response, Database $dbForProject, Delete $deletes, Event $events) { + ->inject('queueForDeletes') + ->inject('queueForEvents') + ->action(function (string $bucketId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents) { $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -313,11 +313,11 @@ App::delete('/v1/storage/buckets/:bucketId') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove bucket from DB'); } - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($bucket); - $events + $queueForEvents ->setParam('bucketId', $bucket->getId()) ->setPayload($response->output($bucket, Response::MODEL_BUCKET)) ; @@ -355,12 +355,12 @@ App::post('/v1/storage/buckets/:bucketId/files') ->inject('response') ->inject('dbForProject') ->inject('user') - ->inject('events') + ->inject('queueForEvents') ->inject('mode') ->inject('deviceFiles') ->inject('deviceLocal') - ->inject('deletes') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $deletes) { + ->inject('queueForDeletes') + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $queueForDeletes) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -636,13 +636,13 @@ App::post('/v1/storage/buckets/:bucketId/files') } } - $events + $queueForEvents ->setParam('bucketId', $bucket->getId()) ->setParam('fileId', $file->getId()) ->setContext('bucket', $bucket) ; - $deletes + $queueForDeletes ->setType(DELETE_TYPE_CACHE_BY_RESOURCE) ->setResource('file/' . $file->getId()) ; @@ -1262,8 +1262,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('dbForProject') ->inject('user') ->inject('mode') - ->inject('events') - ->action(function (string $bucketId, string $fileId, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $events) { + ->inject('queueForEvents') + ->action(function (string $bucketId, string $fileId, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -1329,7 +1329,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $file = Authorization::skip(fn() => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } - $events + $queueForEvents ->setParam('bucketId', $bucket->getId()) ->setParam('fileId', $file->getId()) ->setContext('bucket', $bucket) @@ -1361,11 +1361,11 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->param('fileId', '', new UID(), 'File ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') + ->inject('queueForEvents') ->inject('mode') ->inject('deviceFiles') - ->inject('deletes') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, string $mode, Device $deviceFiles, Delete $deletes) { + ->inject('queueForDeletes') + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceFiles, Delete $queueForDeletes) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { @@ -1402,7 +1402,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') } if ($deviceDeleted) { - $deletes + $queueForDeletes ->setType(DELETE_TYPE_CACHE_BY_RESOURCE) ->setResource('file/' . $fileId) ; @@ -1424,7 +1424,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to delete file from device'); } - $events + $queueForEvents ->setParam('bucketId', $bucket->getId()) ->setParam('fileId', $file->getId()) ->setContext('bucket', $bucket) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 572b6f02a8..b4e59a1f6b 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -57,8 +57,8 @@ App::post('/v1/teams') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); @@ -108,10 +108,10 @@ App::post('/v1/teams') $dbForProject->deleteCachedDocument('users', $user->getId()); } - $events->setParam('teamId', $team->getId()); + $queueForEvents->setParam('teamId', $team->getId()); if (!empty($user->getId())) { - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); } $response @@ -211,8 +211,8 @@ App::put('/v1/teams/:teamId') ->param('name', null, new Text(128), 'New team name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); @@ -224,7 +224,7 @@ App::put('/v1/teams/:teamId') ->setAttribute('name', $name) ->setAttribute('search', implode(' ', [$teamId, $name]))); - $events->setParam('teamId', $team->getId()); + $queueForEvents->setParam('teamId', $team->getId()); $response->dynamic($team, Response::MODEL_TEAM); }); @@ -245,9 +245,9 @@ App::delete('/v1/teams/:teamId') ->param('teamId', '', new UID(), 'Team ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->inject('deletes') - ->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes) { + ->inject('queueForEvents') + ->inject('queueForDeletes') + ->action(function (string $teamId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { $team = $dbForProject->getDocument('teams', $teamId); @@ -271,11 +271,11 @@ App::delete('/v1/teams/:teamId') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB'); } - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($team); - $events + $queueForEvents ->setParam('teamId', $team->getId()) ->setPayload($response->output($team, Response::MODEL_TEAM)) ; @@ -310,9 +310,9 @@ App::post('/v1/teams/:teamId/memberships') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('mails') - ->inject('events') - ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $mails, Event $events) { + ->inject('queueForMail') + ->inject('queueForEvents') + ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMail, Event $queueForEvents) { $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); @@ -431,7 +431,7 @@ App::post('/v1/teams/:teamId/memberships') $url = Template::unParseURL($url); if (!$isPrivilegedUser && !$isAppUser) { // No need of confirmation when in admin or app mode - $mails + $queueForMail ->setType(MAIL_TYPE_INVITATION) ->setRecipient($email) ->setUrl($url) @@ -443,7 +443,7 @@ App::post('/v1/teams/:teamId/memberships') ; } - $events + $queueForEvents ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()) ; @@ -601,8 +601,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('events') - ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -638,7 +638,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') */ $dbForProject->deleteCachedDocument('users', $profile->getId()); - $events + $queueForEvents ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()); @@ -676,8 +676,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('dbForProject') ->inject('project') ->inject('geodb') - ->inject('events') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $events) { + ->inject('queueForEvents') + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -761,7 +761,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1))); - $events + $queueForEvents ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()) ; @@ -803,8 +803,8 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') ->param('membershipId', '', new UID(), 'Membership ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -843,7 +843,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); } - $events + $queueForEvents ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()) ->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP)) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index dce493b024..bb3433e207 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -36,7 +36,7 @@ use MaxMind\Db\Reader; use Utopia\Validator\Integer; /** TODO: Remove function when we move to using utopia/platform */ -function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Database $dbForProject, Event $events): Document +function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Database $dbForProject, Event $queueForEvents): Document { $hashOptionsObject = (\is_string($hashOptions)) ? \json_decode($hashOptions, true) : $hashOptions; // Cast to JSON array @@ -78,7 +78,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e throw new Exception(Exception::USER_ALREADY_EXISTS); } - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); return $user; } @@ -105,9 +105,9 @@ App::post('/v1/users') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Database $dbForProject, Event $events) { - $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $dbForProject, $events); + ->inject('queueForEvents') + ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { + $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -135,9 +135,9 @@ App::post('/v1/users/bcrypt') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $events) { - $user = createUser('bcrypt', '{}', $userId, $email, $password, null, $name, $dbForProject, $events); + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { + $user = createUser('bcrypt', '{}', $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -165,9 +165,9 @@ App::post('/v1/users/md5') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $events) { - $user = createUser('md5', '{}', $userId, $email, $password, null, $name, $dbForProject, $events); + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { + $user = createUser('md5', '{}', $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -195,9 +195,9 @@ App::post('/v1/users/argon2') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $events) { - $user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $dbForProject, $events); + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { + $user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -226,15 +226,15 @@ App::post('/v1/users/sha') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { $options = '{}'; if (!empty($passwordVersion)) { $options = '{"version":"' . $passwordVersion . '"}'; } - $user = createUser('sha', $options, $userId, $email, $password, null, $name, $dbForProject, $events); + $user = createUser('sha', $options, $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -262,9 +262,9 @@ App::post('/v1/users/phpass') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $events) { - $user = createUser('phpass', '{}', $userId, $email, $password, null, $name, $dbForProject, $events); + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { + $user = createUser('phpass', '{}', $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -297,8 +297,8 @@ App::post('/v1/users/scrypt') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $passwordSalt, int $passwordCpu, int $passwordMemory, int $passwordParallel, int $passwordLength, string $name, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $passwordSalt, int $passwordCpu, int $passwordMemory, int $passwordParallel, int $passwordLength, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { $options = [ 'salt' => $passwordSalt, 'costCpu' => $passwordCpu, @@ -307,7 +307,7 @@ App::post('/v1/users/scrypt') 'length' => $passwordLength ]; - $user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $dbForProject, $events); + $user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -338,9 +338,9 @@ App::post('/v1/users/scrypt-modified') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Database $dbForProject, Event $events) { - $user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $dbForProject, $events); + ->inject('queueForEvents') + ->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { + $user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -632,8 +632,8 @@ App::patch('/v1/users/:userId/status') ->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, bool $status, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, bool $status, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -643,7 +643,7 @@ App::patch('/v1/users/:userId/status') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status)); - $events + $queueForEvents ->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); @@ -668,8 +668,8 @@ App::patch('/v1/users/:userId/verification') ->param('emailVerification', false, new Boolean(), 'User email verification status.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -679,7 +679,7 @@ App::patch('/v1/users/:userId/verification') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); - $events + $queueForEvents ->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); @@ -704,8 +704,8 @@ App::patch('/v1/users/:userId/verification/phone') ->param('phoneVerification', false, new Boolean(), 'User phone verification status.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, bool $phoneVerification, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, bool $phoneVerification, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -715,7 +715,7 @@ App::patch('/v1/users/:userId/verification/phone') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification)); - $events + $queueForEvents ->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); @@ -741,8 +741,8 @@ App::patch('/v1/users/:userId/name') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $name, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $name, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -757,7 +757,7 @@ App::patch('/v1/users/:userId/name') $user = $dbForProject->updateDocument('users', $user->getId(), $user); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -782,8 +782,8 @@ App::patch('/v1/users/:userId/password') ->param('password', '', new Password(), 'New user password. Must be at least 8 chars.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $password, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $password, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -799,7 +799,7 @@ App::patch('/v1/users/:userId/password') $user = $dbForProject->updateDocument('users', $user->getId(), $user); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -824,8 +824,8 @@ App::patch('/v1/users/:userId/email') ->param('email', '', new Email(), 'User email.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $email, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $email, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -846,7 +846,7 @@ App::patch('/v1/users/:userId/email') throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -870,8 +870,8 @@ App::patch('/v1/users/:userId/phone') ->param('number', '', new Phone(), 'User phone number.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $number, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $number, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -891,7 +891,7 @@ App::patch('/v1/users/:userId/phone') throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS); } - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -916,8 +916,8 @@ App::patch('/v1/users/:userId/verification') ->param('emailVerification', false, new Boolean(), 'User email verification status.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -927,7 +927,7 @@ App::patch('/v1/users/:userId/verification') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); - $events->setParam('userId', $user->getId()); + $queueForEvents->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -949,8 +949,8 @@ App::patch('/v1/users/:userId/prefs') ->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, array $prefs, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, array $prefs, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -960,7 +960,7 @@ App::patch('/v1/users/:userId/prefs') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); - $events + $queueForEvents ->setParam('userId', $user->getId()); $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); @@ -984,8 +984,8 @@ App::delete('/v1/users/:userId/sessions/:sessionId') ->param('sessionId', '', new UID(), 'Session ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, string $sessionId, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, string $sessionId, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -1002,7 +1002,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $dbForProject->deleteDocument('sessions', $session->getId()); $dbForProject->deleteCachedDocument('users', $user->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setParam('sessionId', $sessionId); @@ -1026,8 +1026,8 @@ App::delete('/v1/users/:userId/sessions') ->param('userId', '', new UID(), 'User ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $userId, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $userId, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -1045,7 +1045,7 @@ App::delete('/v1/users/:userId/sessions') $dbForProject->deleteCachedDocument('users', $user->getId()); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setPayload($response->output($user, Response::MODEL_USER)); @@ -1069,9 +1069,9 @@ App::delete('/v1/users/:userId') ->param('userId', '', new UID(), 'User ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->inject('deletes') - ->action(function (string $userId, Response $response, Database $dbForProject, Event $events, Delete $deletes) { + ->inject('queueForEvents') + ->inject('queueForDeletes') + ->action(function (string $userId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { $user = $dbForProject->getDocument('users', $userId); @@ -1084,11 +1084,11 @@ App::delete('/v1/users/:userId') $dbForProject->deleteDocument('users', $userId); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($clone); - $events + $queueForEvents ->setParam('userId', $user->getId()) ->setPayload($response->output($clone, Response::MODEL_USER)); diff --git a/app/controllers/general.php b/app/controllers/general.php index fb41689487..1e894d087a 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -51,7 +51,7 @@ App::init() ->inject('locale') ->inject('clients') ->inject('servers') - ->inject('certificates') + ->inject('queueForCertificates') ->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients, array $servers, Certificate $queueForCertificates) { /* * Request format diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index db35cebc65..51e791005e 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -97,15 +97,15 @@ App::init() ->inject('response') ->inject('project') ->inject('user') - ->inject('events') - ->inject('audits') - ->inject('mails') + ->inject('queueForEvents') + ->inject('queueForAudits') + ->inject('queueForMail') ->inject('usage') - ->inject('deletes') - ->inject('database') + ->inject('queueForDeletes') + ->inject('queueForDatabase') ->inject('dbForProject') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Mail $mails, Stats $usage, Delete $deletes, EventDatabase $database, Database $dbForProject, string $mode) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMail, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, string $mode) use ($databaseListener) { $route = $utopia->match($request); @@ -174,16 +174,16 @@ App::init() /* * Background Jobs */ - $events + $queueForEvents ->setEvent($route->getLabel('event', '')) ->setProject($project) ->setUser($user); - $mails + $queueForMail ->setProject($project) ->setUser($user); - $audits + $queueForAudits ->setMode($mode) ->setUserAgent($request->getUserAgent('')) ->setIP($request->getIP()) @@ -199,8 +199,8 @@ App::init() ->setParam('project.{scope}.network.inbound', 0) ->setParam('project.{scope}.network.outbound', 0); - $deletes->setProject($project); - $database->setProject($project); + $queueForDeletes->setProject($project); + $queueForDatabase->setProject($project); $dbForProject->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, Document $document) => $databaseListener($event, $document, $usage)); @@ -325,33 +325,33 @@ App::shutdown() ->inject('request') ->inject('response') ->inject('project') - ->inject('events') - ->inject('audits') + ->inject('queueForEvents') + ->inject('queueForAudits') ->inject('usage') - ->inject('deletes') - ->inject('database') + ->inject('queueForDeletes') + ->inject('queueForDatabase') ->inject('mode') ->inject('dbForProject') ->inject('queueForFunctions') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Event $events, Audit $audits, Stats $usage, Delete $deletes, EventDatabase $database, string $mode, Database $dbForProject, Func $queueForFunctions) use ($parseLabel) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Event $queueForEvents, Audit $queueForAudits, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, string $mode, Database $dbForProject, Func $queueForFunctions) use ($parseLabel) { $responsePayload = $response->getPayload(); - if (!empty($events->getEvent())) { - if (empty($events->getPayload())) { - $events->setPayload($responsePayload); + if (!empty($queueForEvents->getEvent())) { + if (empty($queueForEvents->getPayload())) { + $queueForEvents->setPayload($responsePayload); } /** * Trigger functions. */ $queueForFunctions - ->from($events) + ->from($queueForEvents) ->trigger(); /** * Trigger webhooks. */ - $events + $queueForEvents ->setClass(Event::WEBHOOK_CLASS_NAME) ->setQueue(Event::WEBHOOK_QUEUE_NAME) ->trigger(); @@ -360,12 +360,12 @@ App::shutdown() * Trigger realtime. */ if ($project->getId() !== 'console') { - $allEvents = Event::generateEvents($events->getEvent(), $events->getParams()); - $payload = new Document($events->getPayload()); + $allEvents = Event::generateEvents($queueForEvents->getEvent(), $queueForEvents->getParams()); + $payload = new Document($queueForEvents->getPayload()); - $db = $events->getContext('database'); - $collection = $events->getContext('collection'); - $bucket = $events->getContext('bucket'); + $db = $queueForEvents->getContext('database'); + $collection = $queueForEvents->getContext('collection'); + $bucket = $queueForEvents->getContext('bucket'); $target = Realtime::fromPayload( // Pass first, most verbose event pattern @@ -379,13 +379,13 @@ App::shutdown() Realtime::send( projectId: $target['projectId'] ?? $project->getId(), - payload: $events->getPayload(), + payload: $queueForEvents->getPayload(), events: $allEvents, channels: $target['channels'], roles: $target['roles'], options: [ 'permissionsChanged' => $target['permissionsChanged'], - 'userId' => $events->getParam('userId') + 'userId' => $queueForEvents->getParam('userId') ] ); } @@ -393,7 +393,7 @@ App::shutdown() $route = $utopia->match($request); $requestParams = $route->getParamsValues(); - $user = $audits->getUser(); + $user = $queueForAudits->getUser(); /** * Audit labels @@ -402,7 +402,7 @@ App::shutdown() if (!empty($pattern)) { $resource = $parseLabel($pattern, $responsePayload, $requestParams, $user); if (!empty($resource) && $resource !== $pattern) { - $audits->setResource($resource); + $queueForAudits->setResource($resource); } } @@ -410,31 +410,31 @@ App::shutdown() if (!empty($pattern)) { $userId = $parseLabel($pattern, $responsePayload, $requestParams, $user); $user = $dbForProject->getDocument('users', $userId); - $audits->setUser($user); + $queueForAudits->setUser($user); } - if (!empty($audits->getResource()) && !empty($audits->getUser()->getId())) { + if (!empty($queueForAudits->getResource()) && !empty($queueForAudits->getUser()->getId())) { /** * audits.payload is switched to default true * in order to auto audit payload for all endpoints */ $pattern = $route->getLabel('audits.payload', true); if (!empty($pattern)) { - $audits->setPayload($responsePayload); + $queueForAudits->setPayload($responsePayload); } - foreach ($events->getParams() as $key => $value) { - $audits->setParam($key, $value); + foreach ($queueForEvents->getParams() as $key => $value) { + $queueForAudits->setParam($key, $value); } - $audits->trigger(); + $queueForAudits->trigger(); } - if (!empty($deletes->getType())) { - $deletes->trigger(); + if (!empty($queueForDeletes->getType())) { + $queueForDeletes->trigger(); } - if (!empty($database->getType())) { - $database->trigger(); + if (!empty($queueForDatabase->getType())) { + $queueForDatabase->trigger(); } /** diff --git a/app/init.php b/app/init.php index bea032ab75..72ac685569 100644 --- a/app/init.php +++ b/app/init.php @@ -853,34 +853,31 @@ App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en'))) App::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); -App::setResource('messaging', function (Connection $queue) { +App::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); -App::setResource('mails', function (Connection $queue) { +App::setResource('queueForMail', function (Connection $queue) { return new Mail($queue); }, ['queue']); -App::setResource('builds', function (Connection $queue) { +App::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); -App::setResource('database', function (Connection $queue) { +App::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); -App::setResource('deletes', function (Connection $queue) { +App::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); -App::setResource('events', function (Connection $queue) { +App::setResource('queueForEvents', function (Connection $queue) { return new Event('', '', $queue); }, ['queue']); -App::setResource('audits', function (Connection $queue) { +App::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); }, ['queue']); -App::setResource('events', function (Connection $queue) { - return new Event('', '', $queue); -}, ['queue']); App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -App::setResource('certificates', function (Connection $queue) { +App::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); App::setResource('usage', function ($register) { diff --git a/app/worker.php b/app/worker.php index 33fe128a67..613a155297 100644 --- a/app/worker.php +++ b/app/worker.php @@ -90,7 +90,7 @@ Server::setResource('cache', function (Registry $register) { return new Cache(new Sharding($adapters)); }, ['register']); -Server::setResource('database', function (Registry $register) { +Server::setResource('queueForDatabase', function (Registry $register) { $pools = $register->get('pools'); return new EventDatabase( $pools @@ -103,67 +103,33 @@ Server::setResource('database', function (Registry $register) { Server::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); -Server::setResource('messaging', function (Connection $queue) { +Server::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); -Server::setResource('mails', function (Connection $queue) { +Server::setResource('queueForMail', function (Connection $queue) { return new Mail($queue); }, ['queue']); -Server::setResource('builds', function (Connection $queue) { +Server::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); -Server::setResource('database', function (Connection $queue) { +Server::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); -Server::setResource('deletes', function (Connection $queue) { +Server::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); -Server::setResource('events', function (Connection $queue) { +Server::setResource('queueForEvents', function (Connection $queue) { return new Event('', '', $queue); }, ['queue']); -Server::setResource('audits', function (Connection $queue) { +Server::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); }, ['queue']); -Server::setResource('events', function (Connection $queue) { - return new Event('', '', $queue); -}, ['queue']); Server::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -Server::setResource('certificates', function (Connection $queue) { +Server::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); -Server::setResource('events', function (Registry $register) { - $pools = $register->get('pools'); - return new Event( - '', - '', - $pools - ->get('queue') - ->pop() - ->getResource() - ); -}, ['register']); - -Server::setResource('audits', function (Registry $register) { - $pools = $register->get('pools'); - return new Audit( - $pools - ->get('queue') - ->pop() - ->getResource() - ); -}, ['register']); - -Server::setResource('certificates', function (Registry $register) { - $pools = $register->get('pools'); - return new Certificate( - $pools - ->get('queue') - ->pop() - ->getResource() - ); -}, ['register']); Server::setResource('logger', function ($register) { return $register->get('logger'); diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index a632e3bfb0..7bbef0fb08 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -162,12 +162,12 @@ class Func extends Event /** * Generate a function event from a base event * - * @param Event $event + * @param Event $queueForEvents * * @return self * */ - public function from(Event $event): self + public function from(Event $queueForEvents): self { $this->project = $event->getProject(); $this->user = $event->getUser(); diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 7cba04315e..4327ca30eb 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -28,59 +28,59 @@ class Maintenance extends Action $this ->desc('Schedules maintenance tasks and publishes them to queues') ->inject('dbForConsole') - ->inject('certificates') - ->inject('deletes') - ->callback(fn (Database $dbForConsole, Certificate $certificates, Delete $deletes) => $this->action($dbForConsole, $certificates, $deletes)); + ->inject('queueForCertificates') + ->inject('queueForDeletes') + ->callback(fn (Database $dbForConsole, Certificate $queueForCertificates, Delete $queueForDeletes) => $this->action($dbForConsole, $queueForCertificates, $queueForDeletes)); } - public function action(Database $dbForConsole, Certificate $certificates, Delete $deletes): void + public function action(Database $dbForConsole, Certificate $queueForCertificates, Delete $queueForDeletes): void { Console::title('Maintenance V1'); Console::success(APP_NAME . ' maintenance process v1 has started'); - function notifyDeleteExecutionLogs(int $interval, Delete $deletes) + function notifyDeleteExecutionLogs(int $interval, Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_EXECUTIONS) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteAbuseLogs(int $interval, Delete $deletes) + function notifyDeleteAbuseLogs(int $interval, Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_ABUSE) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteAuditLogs(int $interval, Delete $deletes) + function notifyDeleteAuditLogs(int $interval, Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_AUDIT) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteUsageStats(int $usageStatsRetentionHourly, Delete $deletes) + function notifyDeleteUsageStats(int $usageStatsRetentionHourly, Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_USAGE) ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); } - function notifyDeleteConnections(Delete $deletes) + function notifyDeleteConnections(Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_REALTIME) ->setDatetime(DateTime::addSeconds(new \DateTime(), -60)) ->trigger(); } - function notifyDeleteExpiredSessions(Delete $deletes) + function notifyDeleteExpiredSessions(Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_SESSIONS) ->trigger(); } @@ -111,19 +111,19 @@ class Maintenance extends Action } } - function notifyDeleteCache($interval, Delete $deletes) + function notifyDeleteCache($interval, Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } - function notifyDeleteSchedules($interval, Delete $deletes) + function notifyDeleteSchedules($interval, Delete $queueForDeletes) { - ($deletes) + ($queueForDeletes) ->setType(DELETE_TYPE_SCHEDULES) ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); @@ -139,19 +139,19 @@ class Maintenance extends Action $cacheRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days $schedulesDeletionRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day - Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole, $deletes, $certificates) { + Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole, $queueForDeletes, $queueForCertificates) { $time = DateTime::now(); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); - notifyDeleteExecutionLogs($executionLogsRetention, $deletes); - notifyDeleteAbuseLogs($abuseLogsRetention, $deletes); - notifyDeleteAuditLogs($auditLogRetention, $deletes); - notifyDeleteUsageStats($usageStatsRetentionHourly, $deletes); - notifyDeleteConnections($deletes); - notifyDeleteExpiredSessions($deletes); - renewCertificates($dbForConsole, $certificates); - notifyDeleteCache($cacheRetention, $deletes); - notifyDeleteSchedules($schedulesDeletionRetention, $deletes); + notifyDeleteExecutionLogs($executionLogsRetention, $queueForDeletes); + notifyDeleteAbuseLogs($abuseLogsRetention, $queueForDeletes); + notifyDeleteAuditLogs($auditLogRetention, $queueForDeletes); + notifyDeleteUsageStats($usageStatsRetentionHourly, $queueForDeletes); + notifyDeleteConnections($queueForDeletes); + notifyDeleteExpiredSessions($queueForDeletes); + renewCertificates($dbForConsole, $queueForCertificates); + notifyDeleteCache($cacheRetention, $queueForDeletes); + notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes); }, $interval); } } From 98dc1db878183785838994bd5b25aaad21829411 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 23 May 2023 16:43:03 +0300 Subject: [PATCH 021/144] re-working worker flow --- app/cli.php | 25 - app/controllers/shared/api.php | 239 +- app/init.php | 218 +- app/worker.php | 88 +- app/workers/audits.php | 10 +- app/workers/builds.php | 333 +- app/workers/certificates.php | 409 +-- app/workers/databases.php | 499 +-- app/workers/deletes.php | 1367 +++++---- app/workers/functions.php | 89 +- app/workers/mails.php | 124 +- composer.lock | 5205 -------------------------------- docker-compose.yml | 1 - 13 files changed, 1848 insertions(+), 6759 deletions(-) delete mode 100644 composer.lock diff --git a/app/cli.php b/app/cli.php index b327acce28..5da82fa7de 100644 --- a/app/cli.php +++ b/app/cli.php @@ -125,31 +125,6 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $getProjectDB; }, ['pools', 'dbForConsole', 'cache']); -CLI::setResource('influxdb', function (Registry $register) { - $client = $register->get('influxdb'); - /** @var InfluxDB\Client $client */ - $attempts = 0; - $max = 10; - $sleep = 1; - - do { // check if telegraf database is ready - try { - $attempts++; - $database = $client->selectDB('telegraf'); - if (in_array('telegraf', $client->listDatabases())) { - break; // leave the do-while if successful - } - } catch (\Throwable $th) { - Console::warning("InfluxDB not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('InfluxDB database not ready yet'); - } - sleep($sleep); - } - } while ($attempts < $max); - return $database; -}, ['register']); - CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 51e791005e..6119eebd64 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -7,17 +7,16 @@ use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Mail; +use Appwrite\Extend\Exception; +use Appwrite\Event\Usage; use Appwrite\Messaging\Adapter\Realtime; -use Appwrite\Usage\Stats; use Appwrite\Utopia\Response; use Appwrite\Utopia\Request; use Utopia\App; -use Appwrite\Extend\Exception; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; -use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -49,43 +48,101 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar return $label; }; -$databaseListener = function (string $event, Document $document, Stats $usage) { - $multiplier = 1; +$databaseListener = function (string $event, Document $document, Document $project, Usage $queueForUsage, Database $dbForProject) { + + $value = 1; if ($event === Database::EVENT_DOCUMENT_DELETE) { - $multiplier = -1; + $value = -1; } - $collection = $document->getCollection(); - switch ($collection) { - case 'users': - $usage->setParam('users.{scope}.count.total', 1 * $multiplier); + switch (true) { + case $document->getCollection() === 'teams': + $queueForUsage + ->addMetric(METRIC_TEAMS, $value); // per project break; - case 'databases': - $usage->setParam('databases.{scope}.count.total', 1 * $multiplier); + case $document->getCollection() === 'users': + $queueForUsage + ->addMetric(METRIC_USERS, $value); // per project + if ($event === Database::EVENT_DOCUMENT_DELETE) { + $queueForUsage + ->addReduce($document); + } break; - case 'buckets': - $usage->setParam('buckets.{scope}.count.total', 1 * $multiplier); + case $document->getCollection() === 'sessions': // sessions + $queueForUsage + ->addMetric(METRIC_SESSIONS, $value); //per project break; - case 'deployments': - $usage->setParam('deployments.{scope}.storage.size', $document->getAttribute('size') * $multiplier); + case $document->getCollection() === 'databases': // databases + $queueForUsage + ->addMetric(METRIC_DATABASES, $value); // per project + + if ($event === Database::EVENT_DOCUMENT_DELETE) { + $queueForUsage + ->addReduce($document); + } + break; + case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections + $parts = explode('_', $document->getCollection()); + $databaseInternalId = $parts[1] ?? 0; + $queueForUsage + ->addMetric(METRIC_COLLECTIONS, $value) // per project + ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database + ; + + if ($event === Database::EVENT_DOCUMENT_DELETE) { + $queueForUsage + ->addReduce($document); + } + break; + case str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_'): //documents + $parts = explode('_', $document->getCollection()); + $databaseInternalId = $parts[1] ?? 0; + $collectionInternalId = $parts[3] ?? 0; + $queueForUsage + ->addMetric(METRIC_DOCUMENTS, $value) // per project + ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), $value) // per database + ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), $value); // per collection + break; + case $document->getCollection() === 'buckets': //buckets + $queueForUsage + ->addMetric(METRIC_BUCKETS, $value); // per project + if ($event === Database::EVENT_DOCUMENT_DELETE) { + $queueForUsage + ->addReduce($document); + } + break; + case str_starts_with($document->getCollection(), 'bucket_'): // files + $parts = explode('_', $document->getCollection()); + $bucketInternalId = $parts[1]; + $queueForUsage + ->addMetric(METRIC_FILES, $value) // per project + ->addMetric(METRIC_FILES_STORAGE, $document->getAttribute('sizeOriginal') * $value) // per project + ->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES), $value) // per bucket + ->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES_STORAGE), $document->getAttribute('sizeOriginal') * $value); // per bucket + break; + case $document->getCollection() === 'functions': + $queueForUsage + ->addMetric(METRIC_FUNCTIONS, $value); // per project + + if ($event === Database::EVENT_DOCUMENT_DELETE) { + $queueForUsage + ->addReduce($document); + } + break; + case $document->getCollection() === 'deployments': + $queueForUsage + ->addMetric(METRIC_DEPLOYMENTS, $value) // per project + ->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value)// per function + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value);// per function + + break; + case $document->getCollection() === 'executions': + $queueForUsage + ->addMetric(METRIC_EXECUTIONS, $value) // per project + ->addMetric(str_replace('{functionInternalId}', $document->getAttribute('functionInternalId'), METRIC_FUNCTION_ID_EXECUTIONS), $value);// per function break; default: - if (strpos($collection, 'bucket_') === 0) { - $usage - ->setParam('bucketId', $document->getAttribute('bucketId')) - ->setParam('files.{scope}.storage.size', $document->getAttribute('sizeOriginal') * $multiplier) - ->setParam('files.{scope}.count.total', 1 * $multiplier); - } elseif (strpos($collection, 'database_') === 0) { - $usage - ->setParam('databaseId', $document->getAttribute('databaseId')); - if (strpos($collection, '_collection_') !== false) { - $usage - ->setParam('collectionId', $document->getAttribute('$collectionId')) - ->setParam('documents.{scope}.count.total', 1 * $multiplier); - } else { - $usage->setParam('collections.{scope}.count.total', 1 * $multiplier); - } - } break; } }; @@ -103,9 +160,10 @@ App::init() ->inject('usage') ->inject('queueForDeletes') ->inject('queueForDatabase') + ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMail, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, string $mode) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMail, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, Usage $queueForUsage, string $mode) use ($databaseListener) { $route = $utopia->match($request); @@ -124,11 +182,11 @@ App::init() foreach ($abuseKeyLabel as $abuseKey) { $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject); $timeLimit - ->setParam('{userId}', $user->getId()) - ->setParam('{userAgent}', $request->getUserAgent('')) - ->setParam('{ip}', $request->getIP()) - ->setParam('{url}', $request->getHostname() . $route->getPath()) - ->setParam('{method}', $request->getMethod()); + ->setParam('{userId}', $user->getId()) + ->setParam('{userAgent}', $request->getUserAgent('')) + ->setParam('{ip}', $request->getIP()) + ->setParam('{url}', $request->getHostname() . $route->getPath()) + ->setParam('{method}', $request->getMethod()); $timeLimitArray[] = $timeLimit; } @@ -191,20 +249,14 @@ App::init() ->setProject($project) ->setUser($user); - $usage - ->setParam('projectInternalId', $project->getInternalId()) - ->setParam('projectId', $project->getId()) - ->setParam('project.{scope}.network.requests', 1) - ->setParam('httpMethod', $request->getMethod()) - ->setParam('project.{scope}.network.inbound', 0) - ->setParam('project.{scope}.network.outbound', 0); $queueForDeletes->setProject($project); $queueForDatabase->setProject($project); - $dbForProject->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, Document $document) => $databaseListener($event, $document, $usage)); - - $dbForProject->on(Database::EVENT_DOCUMENT_DELETE, fn ($event, Document $document) => $databaseListener($event, $document, $usage)); + $dbForProject + ->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) + ->on(Database::EVENT_DOCUMENT_DELETE, fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) + ; $useCache = $route->getLabel('cache', false); @@ -319,6 +371,45 @@ App::init() } }); +/** + * Limit user session + * + * Delete older sessions if the number of sessions have crossed + * the session limit set for the project + */ +App::shutdown() + ->groups(['session']) + ->inject('utopia') + ->inject('request') + ->inject('response') + ->inject('project') + ->inject('dbForProject') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) { + $sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT; + $session = $response->getPayload(); + $userId = $session['userId'] ?? ''; + if (empty($userId)) { + return; + } + + $user = $dbForProject->getDocument('users', $userId); + if ($user->isEmpty()) { + return; + } + + $sessions = $user->getAttribute('sessions', []); + $count = \count($sessions); + if ($count <= $sessionLimit) { + return; + } + + for ($i = 0; $i < ($count - $sessionLimit); $i++) { + $session = array_shift($sessions); + $dbForProject->deleteDocument('sessions', $session->getId()); + } + $dbForProject->deleteCachedDocument('users', $userId); + }); + App::shutdown() ->groups(['api']) ->inject('utopia') @@ -333,7 +424,8 @@ App::shutdown() ->inject('mode') ->inject('dbForProject') ->inject('queueForFunctions') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Event $queueForEvents, Audit $queueForAudits, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, string $mode, Database $dbForProject, Func $queueForFunctions) use ($parseLabel) { + ->inject('queueForUsage') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Event $queueForEvents, Audit $queueForAudits, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, string $mode, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -458,10 +550,10 @@ App::shutdown() $key = md5($request->getURI() . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER; $data = json_encode([ - 'resourceType' => $resourceType, - 'resource' => $resource, - 'contentType' => $response->getContentType(), - 'payload' => base64_encode($data['payload']), + 'resourceType' => $resourceType, + 'resource' => $resource, + 'contentType' => $response->getContentType(), + 'payload' => base64_encode($data['payload']), ]) ; $signature = md5($data); @@ -489,35 +581,24 @@ App::shutdown() } } - if ( - App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled' - && $project->getId() - && !empty($route->getLabel('sdk.namespace', null)) - ) { // Don't calculate console usage on admin mode - $metric = $route->getLabel('usage.metric', ''); - $usageParams = $route->getLabel('usage.params', []); - if (!empty($metric)) { - $usage->setParam($metric, 1); - foreach ($usageParams as $param) { - $param = $parseLabel($param, $responsePayload, $requestParams, $user); - $parts = explode(':', $param); - if (count($parts) != 2) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Usage params not properly set'); - } - $usage->setParam($parts[0], $parts[1]); + + if ($project->getId() !== 'console') { + if ($mode !== APP_MODE_ADMIN) { + $fileSize = 0; + $file = $request->getFiles('file'); + if (!empty($file)) { + $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; } + + $queueForUsage + ->addMetric(METRIC_NETWORK_REQUESTS, 1) + ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize) + ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()); } - $fileSize = 0; - $file = $request->getFiles('file'); - if (!empty($file)) { - $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; - } - - $usage - ->setParam('project.{scope}.network.inbound', $request->getSize() + $fileSize) - ->setParam('project.{scope}.network.outbound', $response->getSize()) - ->submit(); + $queueForUsage + ->setProject($project) + ->trigger(); } }); diff --git a/app/init.php b/app/init.php index 72ac685569..2378d78032 100644 --- a/app/init.php +++ b/app/init.php @@ -18,47 +18,46 @@ ini_set('display_startup_errors', 1); ini_set('default_socket_timeout', -1); error_reporting(E_ALL); +use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\Auth\Auth; -use Appwrite\SMS\Adapter\Mock; -use Appwrite\SMS\Adapter\Telesign; -use Appwrite\SMS\Adapter\TextMagic; -use Appwrite\SMS\Adapter\Twilio; -use Appwrite\SMS\Adapter\Msg91; -use Appwrite\SMS\Adapter\Vonage; use Appwrite\Event\Audit; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Event\Mail; use Appwrite\Event\Phone; use Appwrite\Event\Delete; +use Appwrite\GraphQL\Schema; use Appwrite\Network\Validator\Email; -use Appwrite\Network\Validator\IP; -use Appwrite\Network\Validator\URL; +use Appwrite\Network\Validator\Origin; use Appwrite\OpenSSL\OpenSSL; use Appwrite\URL\URL as AppwriteURL; -use Appwrite\Usage\Stats; use Utopia\App; -use Utopia\Validator\Range; -use Utopia\Validator\WhiteList; -use Utopia\Database\ID; -use Utopia\Database\Document; +use Utopia\Logger\Logger; +use Utopia\Config\Config; +use Utopia\Database\Helpers\ID; use Utopia\Database\Database; +use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\DatetimeValidator; use Utopia\Database\Validator\Structure; -use Utopia\Logger\Logger; -use Utopia\Config\Config; use Utopia\Locale\Locale; +use Utopia\DSN\DSN; +use Utopia\Messaging\Adapters\SMS\Mock; +use Appwrite\GraphQL\Promises\Adapter\Swoole; +use Utopia\Messaging\Adapters\SMS\Msg91; +use Utopia\Messaging\Adapters\SMS\Telesign; +use Utopia\Messaging\Adapters\SMS\TextMagic; +use Utopia\Messaging\Adapters\SMS\Twilio; +use Utopia\Messaging\Adapters\SMS\Vonage; use Utopia\Registry\Registry; use Utopia\Storage\Device; -use Utopia\DSN\DSN; use Utopia\Storage\Device\Backblaze; use Utopia\Storage\Device\DOSpaces; +use Utopia\Storage\Device\Linode; use Utopia\Storage\Device\Local; use Utopia\Storage\Device\S3; -use Utopia\Storage\Device\Linode; use Utopia\Storage\Device\Wasabi; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Sharding; @@ -79,6 +78,10 @@ use Utopia\CLI\Console; use Utopia\Queue; use Utopia\Queue\Connection; use Utopia\Storage\Storage; +use Utopia\Validator\Range; +use Utopia\Validator\IP; +use Utopia\Validator\URL; +use Utopia\Validator\WhiteList; const APP_NAME = 'Appwrite'; const APP_DOMAIN = 'appwrite.io'; @@ -90,6 +93,8 @@ const APP_MODE_ADMIN = 'admin'; const APP_PAGING_LIMIT = 12; const APP_LIMIT_COUNT = 5000; const APP_LIMIT_USERS = 10000; +const APP_LIMIT_USER_SESSIONS_MAX = 100; +const APP_LIMIT_USER_SESSIONS_DEFAULT = 10; const APP_LIMIT_ANTIVIRUS = 20000000; //20MB const APP_LIMIT_ENCRYPTION = 20000000; //20MB const APP_LIMIT_COMPRESSION = 20000000; //20MB @@ -98,10 +103,11 @@ const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element const APP_LIMIT_SUBQUERY = 1000; const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds +const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 501; -const APP_VERSION_STABLE = '1.1.2'; +const APP_VERSION_STABLE = '1.2.1'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -176,6 +182,39 @@ const APP_AUTH_TYPE_KEY = 'Key'; const APP_AUTH_TYPE_ADMIN = 'Admin'; // Response related const MAX_OUTPUT_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB +// Usage metrics +const METRIC_TEAMS = 'teams'; +const METRIC_USERS = 'users'; +const METRIC_SESSIONS = 'sessions'; +const METRIC_DATABASES = 'databases'; +const METRIC_COLLECTIONS = 'collections'; +const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +const METRIC_DOCUMENTS = 'documents'; +const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; +const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +const METRIC_BUCKETS = 'buckets'; +const METRIC_FILES = 'files'; +const METRIC_FILES_STORAGE = 'files.storage'; +const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; +const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; +const METRIC_FUNCTIONS = 'functions'; +const METRIC_DEPLOYMENTS = 'deployments'; +const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage'; +const METRIC_BUILDS = 'builds'; +const METRIC_BUILDS_STORAGE = 'builds.storage'; +const METRIC_BUILDS_COMPUTE = 'builds.compute'; +const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds'; +const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage'; +const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute'; +const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; +const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; +const METRIC_EXECUTIONS = 'executions'; +const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; +const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; +const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; +const METRIC_NETWORK_REQUESTS = 'network.requests'; +const METRIC_NETWORK_INBOUND = 'network.inbound'; +const METRIC_NETWORK_OUTBOUND = 'network.outbound'; $register = new Registry(); @@ -191,6 +230,7 @@ Config::load('providers', __DIR__ . '/config/providers.php'); Config::load('platforms', __DIR__ . '/config/platforms.php'); Config::load('collections', __DIR__ . '/config/collections.php'); Config::load('runtimes', __DIR__ . '/config/runtimes.php'); +Config::load('usage', __DIR__ . '/config/usage.php'); Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes Config::load('services', __DIR__ . '/config/services.php'); // List of services @@ -578,13 +618,11 @@ $register->set('pools', function () { $schemes = $connection['schemes'] ?? []; $config = []; $dsns = explode(',', $connection['dsns'] ?? ''); - foreach ($dsns as &$dsn) { $dsn = explode('=', $dsn); $name = ($multipe) ? $key . '_' . $dsn[0] : $key; $dsn = $dsn[1] ?? ''; $config[] = $name; - if (empty($dsn)) { //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); continue; @@ -609,7 +647,6 @@ $register->set('pools', function () { * * Resource assignment to an adapter will happen below. */ - switch ($dsnScheme) { case 'mysql': case 'mariadb': @@ -647,7 +684,6 @@ $register->set('pools', function () { $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { // Get Adapter $adapter = null; - switch ($type) { case 'database': $adapter = match ($dsn->getScheme()) { @@ -691,30 +727,6 @@ $register->set('pools', function () { return $group; }); -$register->set('influxdb', function () { - // Register DB connection - $host = App::getEnv('_APP_INFLUXDB_HOST', ''); - $port = App::getEnv('_APP_INFLUXDB_PORT', ''); - - if (empty($host) || empty($port)) { - return; - } - $driver = new InfluxDB\Driver\Curl("http://{$host}:{$port}"); - $client = new InfluxDB\Client($host, $port, '', '', false, false, 5); - $client->setDriver($driver); - - return $client; -}); -$register->set('statsd', function () { - // Register DB connection - $host = App::getEnv('_APP_STATSD_HOST', 'telegraf'); - $port = App::getEnv('_APP_STATSD_PORT', 8125); - - $connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port); - $statsd = new \Domnikl\Statsd\Client($connection); - - return $statsd; -}); $register->set('smtp', function () { $mail = new PHPMailer(true); @@ -744,9 +756,11 @@ $register->set('smtp', function () { return $mail; }); $register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2022-06.mmdb'); + return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2023-01.mmdb'); +}); +$register->set('promiseAdapter', function () { + return new Swoole(); }); - /* * Localization */ @@ -846,7 +860,6 @@ App::setResource('loggerBreadcrumbs', function () { }); App::setResource('register', fn() => $register); - App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en'))); // Queues @@ -877,17 +890,18 @@ App::setResource('queueForAudits', function (Connection $queue) { App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); +App::setResource('queueForUsage', function (Connection $queue) { + return new Usage($queue); +}, ['queue']); App::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); -App::setResource('usage', function ($register) { - return new Stats($register->get('statsd')); -}, ['register']); + App::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), 'name' => 'Current Host', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => $request->getHostname(), ], Document::SET_TYPE_APPEND); @@ -899,7 +913,7 @@ App::setResource('clients', function ($request, $console, $project) { fn ($node) => $node['hostname'], \array_filter( $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); @@ -910,7 +924,7 @@ App::setResource('clients', function ($request, $console, $project) { fn ($node) => $node['hostname'], \array_filter( $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ) ) @@ -1043,7 +1057,7 @@ App::setResource('console', function () { [ '$collection' => ID::custom('platforms'), 'name' => 'Localhost', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => 'localhost', ], // Current host is added on app init ], @@ -1202,7 +1216,97 @@ App::setResource('servers', function () { $languages = array_map(function ($language) { return strtolower($language['name']); - }, $server['languages']); + }, $server['sdks']); return $languages; }); + +App::setResource('promiseAdapter', function ($register) { + return $register->get('promiseAdapter'); +}, ['register']); + +App::setResource('schema', function ($utopia, $dbForProject) { + + $complexity = function (int $complexity, array $args) { + $queries = Query::parseQueries($args['queries'] ?? []); + $query = Query::getByType($queries, Query::TYPE_LIMIT)[0] ?? null; + $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; + + return $complexity * $limit; + }; + + $attributes = function (int $limit, int $offset) use ($dbForProject) { + $attrs = Authorization::skip(fn() => $dbForProject->find('attributes', [ + Query::limit($limit), + Query::offset($offset), + ])); + + return \array_map(function ($attr) { + return $attr->getArrayCopy(); + }, $attrs); + }; + + $urls = [ + 'list' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents"; + }, + 'create' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents"; + }, + 'read' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + 'update' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + 'delete' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + ]; + + $params = [ + 'list' => function (string $databaseId, string $collectionId, array $args) { + return [ 'queries' => $args['queries']]; + }, + 'create' => function (string $databaseId, string $collectionId, array $args) { + $id = $args['id'] ?? 'unique()'; + $permissions = $args['permissions'] ?? null; + + unset($args['id']); + unset($args['permissions']); + + // Order must be the same as the route params + return [ + 'databaseId' => $databaseId, + 'documentId' => $id, + 'collectionId' => $collectionId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + 'update' => function (string $databaseId, string $collectionId, array $args) { + $documentId = $args['id']; + $permissions = $args['permissions'] ?? null; + + unset($args['id']); + unset($args['permissions']); + + // Order must be the same as the route params + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + ]; + + return Schema::build( + $utopia, + $complexity, + $attributes, + $urls, + $params, + ); +}, ['utopia', 'dbForProject']); diff --git a/app/worker.php b/app/worker.php index 613a155297..f987af0671 100644 --- a/app/worker.php +++ b/app/worker.php @@ -11,6 +11,7 @@ use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Phone; +use Appwrite\Event\Usage; use Swoole\Runtime; use Utopia\App; use Utopia\Cache\Adapter\Sharding; @@ -130,7 +131,9 @@ Server::setResource('queueForFunctions', function (Connection $queue) { Server::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); - +Server::setResource('queueForUsage', function (Connection $queue) { + return new Usage($queue); +}, ['queue']); Server::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); @@ -152,7 +155,7 @@ if (empty(App::getEnv('QUEUE'))) { } $adapter = new Swoole($connection, $workerNumber, App::getEnv('QUEUE')); -$server = new Server($adapter); +$server = new Server($adapter); $server ->shutdown() @@ -211,7 +214,7 @@ function getCache(): Cache global $register; $pools = $register->get('pools'); - /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; @@ -226,85 +229,50 @@ function getCache(): Cache return new Cache(new Sharding($adapters)); } -/** - * Get Console DB - * - * @returns Database - */ -function getConsoleDB(): Database -{ - global $register; +Server::setResource('getProjectDB', function (Registry $register, Database $dbForConsole) { + return function (Document $project) use ($register, $dbForConsole) { + /** @var Group $pools */ + $pools = $register->get('pools'); - /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools'); + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); + $dbAdapter = $pools + ->get($project->getAttribute('database')) + ->pop() + ->getResource(); - $database = new Database($dbAdapter, getCache()); - - $database->setNamespace('console'); - - return $database; -} - -/** - * Get Project DB - * - * @param Document $project - * @returns Database - */ -function getProjectDB(Document $project): Database -{ - global $register; - - /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools'); - - if ($project->isEmpty() || $project->getId() === 'console') { - return getConsoleDB(); - } - - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, getCache()); - $database->setNamespace('_' . $project->getInternalId()); - - return $database; -} + $database = new Database($dbAdapter, getCache()); + $database->setNamespace('_' . $project->getInternalId()); + return $database; + }; +}, ['register']); /** * Get Functions Storage Device * @param string $projectId of the project * @return Device */ -function getFunctionsDevice($projectId): Device -{ +Server::setResource('getFunctionsDevice', function (string $projectId) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); -} +}); /** * Get Files Storage Device * @param string $projectId of the project * @return Device */ -function getFilesDevice($projectId): Device -{ +Server::setResource('getFilesDevice', function (string $projectId) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); -} +}); /** * Get Builds Storage Device * @param string $projectId of the project * @return Device */ -function getBuildsDevice($projectId): Device -{ +Server::setResource('getBuildsDevice', function (string $projectId) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); -} +}); diff --git a/app/workers/audits.php b/app/workers/audits.php index 12aa131ccc..5b2571e025 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -12,9 +12,8 @@ use Utopia\Queue\Server; Authorization::disable(); Authorization::setDefaultStatus(false); -Server::setResource('execute', function () { +Server::setResource('execute', function (Database $dbForProject) { return function ( - Database $dbForProject, string $event, array $payload, string $mode, @@ -23,7 +22,7 @@ Server::setResource('execute', function () { string $ip, Document $user, Document $project - ) { + ) use ($dbForProject) { $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); @@ -45,13 +44,13 @@ Server::setResource('execute', function () { ] ); }; -}); +}, ['dbForProject']); $server->job() ->inject('message') ->inject('dbForProject') ->inject('execute') - ->action(function (Message $message, Database $dbForProject, callable $execute) { + ->action(function (Message $message, callable $execute) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -68,7 +67,6 @@ $server->job() $user = new Document($payload['user'] ?? []); $execute( - $dbForProject, $event, $auditPayload, $mode, diff --git a/app/workers/builds.php b/app/workers/builds.php index a264b41144..747b88c88a 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -4,62 +4,74 @@ require_once __DIR__ . '/../worker.php'; use Appwrite\Event\Event; use Appwrite\Event\Func; +use Appwrite\Event\Usage; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Deployment; use Executor\Executor; -use Appwrite\Usage\Stats; +use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\App; use Utopia\CLI\Console; -use Utopia\Database\ID; +use Utopia\Database\Helpers\ID; use Utopia\DSN\DSN; use Utopia\Database\Document; use Utopia\Config\Config; use Utopia\Database\Validator\Authorization; use Utopia\Queue\Message; +use Utopia\Queue\Server; +use Utopia\Registry\Registry; use Utopia\Storage\Storage; Authorization::disable(); Authorization::setDefaultStatus(false); -function buildDeployment(Document $project, Document $function, Document $deployment) -{ - global $register; - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); +Server::setResource('buildDeployment', function (Registry $register, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage) { + return function ( + Document $deployment, + Document $project, + Document $function, + ) use ( + $register, + $dbForProject, + $queueForFunctions, + $queueForUsage +) { - $dbForProject = getProjectDB($project); + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $dbForProject = getProjectDB($project); + $function = $dbForProject->getDocument('functions', $function->getId()); - $function = $dbForProject->getDocument('functions', $function->getId()); - if ($function->isEmpty()) { - throw new Exception('Function not found', 404); - } + if ($function->isEmpty()) { + throw new Exception('Function not found', 404); + } - $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); - if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404); - } + $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); + if ($deployment->isEmpty()) { + throw new Exception('Deployment not found', 404); + } - $runtimes = Config::getParam('runtimes', []); - $key = $function->getAttribute('runtime'); - $runtime = isset($runtimes[$key]) ? $runtimes[$key] : null; - if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); - } + $runtimes = Config::getParam('runtimes', []); + $key = $function->getAttribute('runtime'); + $runtime = $runtimes[$key] ?? null; + if (\is_null($runtime)) { + throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); + } - $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); /** @TODO : move this to the registry or someplace else */ - $device = Storage::DEVICE_LOCAL; - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - } catch (\Exception $e) { - Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } + $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); /** @TODO : move this to the registry or someplace else */ + $device = Storage::DEVICE_LOCAL; + try { + $dsn = new DSN($connection); + $device = $dsn->getScheme(); + } catch (\Exception $e) { + Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } - $buildId = $deployment->getAttribute('buildId', ''); - $startTime = DateTime::now(); - if (empty($buildId)) { - $buildId = ID::unique(); - $build = $dbForProject->createDocument('builds', new Document([ + $buildId = $deployment->getAttribute('buildId', ''); + $startTime = DateTime::now(); + + if (empty($buildId)) { + $buildId = ID::unique(); + $build = $dbForProject->createDocument('builds', new Document([ '$id' => $buildId, '$permissions' => [], 'startTime' => $startTime, @@ -73,25 +85,26 @@ function buildDeployment(Document $project, Document $function, Document $deploy 'stdout' => '', 'stderr' => '', 'duration' => 0 - ])); - $deployment->setAttribute('buildId', $buildId); - $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); - } else { - $build = $dbForProject->getDocument('builds', $buildId); - } + ])); + + $deployment->setAttribute('buildId', $buildId); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + } else { + $build = $dbForProject->getDocument('builds', $buildId); + } /** Request the executor to build the code... */ - $build->setAttribute('status', 'building'); - $build = $dbForProject->updateDocument('builds', $buildId, $build); + $build->setAttribute('status', 'building'); + $build = $dbForProject->updateDocument('builds', $buildId, $build); /** Trigger Webhook */ - $deploymentModel = new Deployment(); + $deploymentModel = new Deployment(); - $pools = $register->get('pools'); - $connection = $pools->get('queue')->pop()->getResource(); + $pools = $register->get('pools'); + $connection = $pools->get('queue')->pop()->getResource(); - $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $connection); - $deploymentUpdate + $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $connection); + $deploymentUpdate ->setProject($project) ->setEvent('functions.[functionId].deployments.[deploymentId].update') ->setParam('functionId', $function->getId()) @@ -100,113 +113,28 @@ function buildDeployment(Document $project, Document $function, Document $deploy ->trigger(); /** Trigger Functions */ - $pools = $register->get('pools'); - $connection = $pools->get('queue')->pop(); + $pools = $register->get('pools'); + $connection = $pools->get('queue')->pop(); - $functions = new Func($connection->getResource()); - $functions + $functions = new Func($connection->getResource()); + $functions ->from($deploymentUpdate) ->trigger(); - $connection->reclaim(); + $connection->reclaim(); /** Trigger Realtime */ - $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ + $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ 'functionId' => $function->getId(), 'deploymentId' => $deployment->getId() - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - - $source = $deployment->getAttribute('path'); - - $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { - $carry[$var->getAttribute('key')] = $var->getAttribute('value'); - return $carry; - }, []); - - try { - $response = $executor->createRuntime( - projectId: $project->getId(), - deploymentId: $deployment->getId(), - source: $source, - image: $runtime['image'], - remove: true, - entrypoint: $deployment->getAttribute('entrypoint'), - workdir: '/usr/code', - destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", - variables: $vars, - commands: [ - 'sh', '-c', - 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ - cd /usr/local/src/ && ./build.sh' - ] - ); - - /** Update the build document */ - $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); - $build->setAttribute('duration', \intval($response['duration'])); - $build->setAttribute('status', $response['status']); - $build->setAttribute('path', $response['path']); - $build->setAttribute('size', $response['size']); - $build->setAttribute('stderr', $response['stderr']); - $build->setAttribute('stdout', $response['stdout']); - - /* Also update the deployment buildTime */ - $deployment->setAttribute('buildTime', $response['duration']); - - Console::success("Build id: $buildId created"); - - $function->setAttribute('scheduleUpdatedAt', DateTime::now()); - - /** Set auto deploy */ - if ($deployment->getAttribute('activate') === true) { - $function->setAttribute('deployment', $deployment->getId()); - $function = $dbForProject->updateDocument('functions', $function->getId(), $function); - } - - /** Update function schedule */ - $dbForConsole = getConsoleDB(); - $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); - - $schedule - ->setAttribute('schedule', $function->getAttribute('schedule')) - ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - - - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - } catch (\Throwable $th) { - $endTime = DateTime::now(); - $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); - $build->setAttribute('duration', $interval->format('%s') + 0); - $build->setAttribute('status', 'failed'); - $build->setAttribute('stderr', $th->getMessage()); - Console::error($th->getMessage()); - } finally { - $build = $dbForProject->updateDocument('builds', $buildId, $build); - - /** - * Send realtime Event - */ + ]); $target = Realtime::fromPayload( - // Pass first, most verbose event pattern + // Pass first, most verbose event pattern event: $allEvents[0], payload: $build, project: $project ); + Realtime::send( projectId: 'console', payload: $build->getArrayCopy(), @@ -215,28 +143,109 @@ function buildDeployment(Document $project, Document $function, Document $deploy roles: $target['roles'] ); - /** Update usage stats */ - if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { - $statsd = $register->get('statsd'); - $usage = new Stats($statsd); - $usage - ->setParam('projectInternalId', $project->getInternalId()) - ->setParam('projectId', $project->getId()) - ->setParam('functionId', $function->getId()) - ->setParam('builds.{scope}.compute', 1) - ->setParam('buildStatus', $build->getAttribute('status', '')) - ->setParam('buildTime', $build->getAttribute('duration')) - ->setParam('buildSize', $build->getAttribute('size')) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0) - ->submit(); + $source = $deployment->getAttribute('path'); + + $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { + $carry[$var->getAttribute('key')] = $var->getAttribute('value'); + return $carry; + }, []); + + try { + $response = $executor->createRuntime( + deploymentId: $deployment->getId(), + projectId: $project->getId(), + source: $source, + image: $runtime['image'], + remove: true, + entrypoint: $deployment->getAttribute('entrypoint'), + workdir: '/usr/code', + destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", + variables: $vars, + commands: [ + 'sh', '-c', + 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ + cd /usr/local/src/ && ./build.sh' + ] + ); + + /** Update the build document */ + $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); + $build->setAttribute('duration', \intval($response['duration'])); + $build->setAttribute('status', $response['status']); + $build->setAttribute('path', $response['path']); + $build->setAttribute('size', $response['size']); + $build->setAttribute('stderr', $response['stderr']); + $build->setAttribute('stdout', $response['stdout']); + + /* Also update the deployment buildTime */ + $deployment->setAttribute('buildTime', $response['duration']); + + Console::success("Build id: $buildId created"); + + $function->setAttribute('scheduleUpdatedAt', DateTime::now()); + + /** Set auto deploy */ + if ($deployment->getAttribute('activate') === true) { + $function->setAttribute('deployment', $deployment->getId()); + $function = $dbForProject->updateDocument('functions', $function->getId(), $function); + } + + /** Update function schedule */ + $dbForConsole = getConsoleDB(); + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); + + $schedule + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + + + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + } catch (\Throwable $th) { + $endTime = DateTime::now(); + $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); + $build->setAttribute('duration', $interval->format('%s') + 0); + $build->setAttribute('status', 'failed'); + $build->setAttribute('stderr', $th->getMessage()); + Console::error($th->getMessage()); + } finally { + $build = $dbForProject->updateDocument('builds', $buildId, $build); + + /** + * Send realtime Event + */ + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project + ); + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + + /** Trigger usage queue */ + $queueForUsage + ->setProject($project) + ->addMetric(METRIC_BUILDS, 1) // per project + ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) + ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) + ->trigger(); } - } -} + }; +}, ['register', 'queueForFunctions', 'queueForUsage']); $server->job() ->inject('message') - ->action(function (Message $message) { + ->inject('buildDeployment') + ->action(function (Message $message, callable $buildDeployment) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -252,7 +261,11 @@ $server->job() case BUILD_TYPE_DEPLOYMENT: case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); - buildDeployment($project, $resource, $deployment); + $buildDeployment( + $project, + $resource, + $deployment + ); break; default: diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 4d9b4a4380..adf207ef37 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -6,28 +6,37 @@ use Appwrite\Event\Mail; use Appwrite\Network\Validator\CNAME; use Utopia\App; use Utopia\CLI\Console; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\ID; +use Utopia\Database\DateTime; +use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Domains\Domain; use Utopia\Queue\Message; use Utopia\Queue\Server; -use Utopia\Database\DateTime; -Authorization::disable(); -Authorization::setDefaultStatus(false); + +Console::title('Certificates V1 Worker'); +Console::success(APP_NAME . ' certificates worker v1 has started'); $database = null; -Server::setResource('execute', function () { +Server::setResource('execute', function (Database $dbForConsole, callable $notifyError, callable $applyCertificateFiles, callable $getRenewDate, callable $issueCertificate, callable $isRenewRequired, callable $validateDomain, callable $saveCertificateDocument, callable $getMainDomain) { return function ( - Database $dbForConsole, Document $document, Domain $domain, bool $skipRenewCheck = false - ) { + ) use ( + $dbForConsole, + $notifyError, + $applyCertificateFiles, + $getRenewDate, + $issueCertificate, + $isRenewRequired, + $validateDomain, + $saveCertificateDocument, + $getMainDomain +) { /** * 1. Read arguments and validate domain * 2. Get main domain @@ -76,13 +85,13 @@ Server::setResource('execute', function () { // Validate domain and DNS records. Skip if job is forced if (!$skipRenewCheck) { - $mainDomain = getMainDomain($dbForConsole); + $mainDomain = $getMainDomain($dbForConsole); $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; - validateDomain($domain, $isMainDomain); + $validateDomain($domain, $isMainDomain); } // If certificate exists already, double-check expiry date. Skip if job is forced - if (!$skipRenewCheck && !isRenewRequired($domain->get())) { + if (!$skipRenewCheck && !$isRenewRequired($domain->get())) { throw new Exception('Renew isn\'t required.'); } @@ -90,7 +99,7 @@ Server::setResource('execute', function () { $folder = ID::unique(); // Generate certificate files using Let's Encrypt - $letsEncryptData = issueCertificate($folder, $domain->get(), $email); + $letsEncryptData = $issueCertificate($folder, $domain->get(), $email); // Command succeeded, store all data into document // We store stderr too, because it may include warnings @@ -100,10 +109,10 @@ Server::setResource('execute', function () { ])); // Give certificates to Traefik - applyCertificateFiles($folder, $domain->get(), $letsEncryptData); + $applyCertificateFiles($folder, $domain->get(), $letsEncryptData); // Update certificate info stored in database - $certificate->setAttribute('renewDate', getRenewDate($domain->get())); + $certificate->setAttribute('renewDate', $getRenewDate($domain->get())); $certificate->setAttribute('attempts', 0); $certificate->setAttribute('issueDate', DateTime::now()); } catch (Throwable $e) { @@ -118,17 +127,16 @@ Server::setResource('execute', function () { $certificate->setAttribute('renewDate', DateTime::now()); // Send email to security email - notifyError($domain->get(), $e->getMessage(), $attempts); + $notifyError($domain->get(), $e->getMessage(), $attempts); } finally { // All actions result in new updatedAt date $certificate->setAttribute('updated', DateTime::now()); // Save all changes we made to certificate document into database - saveCertificateDocument($domain->get(), $certificate, $dbForConsole); + $saveCertificateDocument($domain->get(), $certificate, $dbForConsole); } }; -}); - +}, ['dbForConsole', 'notifyError', 'applyCertificateFiles', 'getRenewDate', 'issueCertificate', 'isRenewRequired', 'validateDomain', 'saveCertificateDocument', 'getMainDomain']); /** * Save certificate data into database. @@ -139,42 +147,50 @@ Server::setResource('execute', function () { * * @return void */ -function saveCertificateDocument(string $domain, Document $certificate, Database $dbForConsole): void -{ - // Check if update or insert required - $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); - if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { - // Merge new data with current data - $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); +Server::setResource('saveCertificateDocument', function (Database $dbForConsole, callable $updateDomainDocuments) { + return function ( + string $domain, + Document $certificate, + ) use ( + $dbForConsole, + $updateDomainDocuments +) { + // Check if update or insert required + $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); + if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { + // Merge new data with current data + $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + } else { + $certificate = $dbForConsole->createDocument('certificates', $certificate); + } - $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); - } else { - $certificate = $dbForConsole->createDocument('certificates', $certificate); - } + $certificateId = $certificate->getId(); + $updateDomainDocuments($certificateId, $domain); + }; +}, ['dbForConsole', 'updateDomainDocuments']); - $certificateId = $certificate->getId(); - updateDomainDocuments($certificateId, $domain, $dbForConsole); -} /** * Get main domain. Needed as we do different checks for main and non-main domains. * * @return null|string Returns main domain. If null, there is no main domain yet. */ -function getMainDomain($dbForConsole): ?string -{ - $envDomain = App::getEnv('_APP_DOMAIN', ''); - if (!empty($envDomain) && $envDomain !== 'localhost') { - return $envDomain; - } else { - $domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]); - if ($domainDocument) { - return $domainDocument->getAttribute('domain'); +Server::setResource('getMainDomain', function (Database $dbForConsole) { + return function () use ($dbForConsole) { + $envDomain = App::getEnv('_APP_DOMAIN', ''); + if (!empty($envDomain) && $envDomain !== 'localhost') { + return $envDomain; + } else { + $domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]); + if ($domainDocument) { + return $domainDocument->getAttribute('domain'); + } } - } - return null; -} + return null; + }; +}, ['dbForConsole']); /** * Internal domain validation functionality to prevent unnecessary attempts failed from Let's Encrypt side. We check: @@ -186,112 +202,125 @@ function getMainDomain($dbForConsole): ?string * * @return void */ -function validateDomain(Domain $domain, bool $isMainDomain): void -{ - if (empty($domain->get())) { - throw new Exception('Missing certificate domain.'); - } - - if (!$domain->isKnown() || $domain->isTest()) { - throw new Exception('Unknown public suffix for domain.'); - } - - if (!$isMainDomain) { - // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? - // Validate if domain target is properly configured - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); - - if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); +Server::setResource('validateDomain', function () { + return function ( + Domain $domain, + bool $isMainDomain, + ) { + if (empty($domain->get())) { + throw new Exception('Missing certificate domain.'); } - // Verify domain with DNS records - $validator = new CNAME($target->get()); - if (!$validator->isValid($domain->get())) { - throw new Exception('Failed to verify domain DNS records.'); + if (!$domain->isKnown() || $domain->isTest()) { + throw new Exception('Unknown public suffix for domain.'); } - } else { - // Main domain validation - // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? - } -} + + if (!$isMainDomain) { + // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? + // Validate if domain target is properly configured + $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + + if (!$target->isKnown() || $target->isTest()) { + throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); + } + + // Verify domain with DNS records + $validator = new CNAME($target->get()); + if (!$validator->isValid($domain->get())) { + throw new Exception('Failed to verify domain DNS records.'); + } + } else { + // Main domain validation + // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? + } + }; +}); /** * Reads expiry date of certificate from file and decides if renewal is required or not. * * @param string $domain Domain for which we check certificate file - * * @return bool True, if certificate needs to be renewed */ -function isRenewRequired(string $domain): bool -{ - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; - if (\file_exists($certPath)) { - $validTo = null; +Server::setResource('isRenewRequired', function () { + return function ( + string $domain + ) { + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + if (\file_exists($certPath)) { + $validTo = null; - $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? 0; + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? 0; - if (empty($validTo)) { - throw new Exception('Unable to read certificate file (cert.pem).'); + if (empty($validTo)) { + throw new Exception('Unable to read certificate file (cert.pem).'); + } + + // LetsEncrypt allows renewal 30 days before expiry + $expiryInAdvance = (60 * 60 * 24 * 30); + if ($validTo - $expiryInAdvance > \time()) { + return false; + } } - // LetsEncrypt allows renewal 30 days before expiry - $expiryInAdvance = (60 * 60 * 24 * 30); - if ($validTo - $expiryInAdvance > \time()) { - return false; - } - } - - return true; -} + return true; + }; +}); /** * LetsEncrypt communication to issue certificate (using certbot CLI) * * @param string $folder Folder into which certificates should be generated * @param string $domain Domain to generate certificate for - * * @return array Named array with keys 'stdout' and 'stderr', both string */ -function issueCertificate(string $folder, string $domain, string $email): array -{ - $stdout = ''; - $stderr = ''; +Server::setResource('issueCertificate', function () { + return function ( + string $folder, + string $domain, + string $email, + ) { - $staging = (App::isProduction()) ? '' : ' --dry-run'; - $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + $stdout = ''; + $stderr = ''; + + $staging = (App::isProduction()) ? '' : ' --dry-run'; + $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" . " --email " . $email . " --cert-name " . $folder . " -w " . APP_STORAGE_CERTIFICATES . " -d {$domain}", '', $stdout, $stderr); - // Unexpected error, usually 5XX, API limits, ... - if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $stderr); - } + // Unexpected error, usually 5XX, API limits, ... + if ($exit !== 0) { + throw new Exception('Failed to issue a certificate with message: ' . $stderr); + } - return [ - 'stdout' => $stdout, - 'stderr' => $stderr - ]; -} + return [ + 'stdout' => $stdout, + 'stderr' => $stderr + ]; + }; +}); /** * Read new renew date from certificate file generated by Let's Encrypt * * @param string $domain Domain which certificate was generated for - * * @return string */ -function getRenewDate(string $domain): string -{ - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; - $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? null; - $dt = (new \DateTime())->setTimestamp($validTo); - return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days -} +Server::setResource('getRenewDate', function () { + return function ( + string $domain, + ) { + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? null; + $dt = (new \DateTime())->setTimestamp($validTo); + return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days + }; +}); /** * Method to take files from Let's Encrypt, and put it into Traefik. @@ -299,48 +328,52 @@ function getRenewDate(string $domain): string * @param string $domain Domain which certificate was generated for * @param string $folder Folder in which certificates were generated * @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error - * * @return void */ -function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void -{ - // Prepare folder in storage for domain - $path = APP_STORAGE_CERTIFICATES . '/' . $domain; - if (!\is_readable($path)) { - if (!\mkdir($path, 0755, true)) { - throw new Exception('Failed to create path for certificate.'); +Server::setResource('applyCertificateFiles', function () { + return function ( + string $folder, + string $domain, + array $letsEncryptData, + ) { + // Prepare folder in storage for domain + $path = APP_STORAGE_CERTIFICATES . '/' . $domain; + if (!\is_readable($path)) { + if (!\mkdir($path, 0755, true)) { + throw new Exception('Failed to create path for certificate.'); + } } - } - // Move generated files - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } + // Move generated files + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } - $config = \implode(PHP_EOL, [ - "tls:", - " certificates:", - " - certFile: /storage/certificates/{$domain}/fullchain.pem", - " keyFile: /storage/certificates/{$domain}/privkey.pem" - ]); + $config = \implode(PHP_EOL, [ + "tls:", + " certificates:", + " - certFile: /storage/certificates/{$domain}/fullchain.pem", + " keyFile: /storage/certificates/{$domain}/privkey.pem" + ]); - // Save configuration into Traefik using our new cert files - if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { - throw new Exception('Failed to save Traefik configuration.'); - } -} + // Save configuration into Traefik using our new cert files + if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { + throw new Exception('Failed to save Traefik configuration.'); + } + }; +}); /** * Method to make sure information about error is delivered to admnistrator. @@ -348,29 +381,32 @@ function applyCertificateFiles(string $folder, string $domain, array $letsEncryp * @param string $domain Domain that caused the error * @param string $errorMessage Verbose error message * @param int $attempt How many times it failed already - * * @return void */ -function notifyError(string $domain, string $errorMessage, int $attempt): void -{ - // Log error into console - Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); +Server::setResource('notifyError', function (Mail $queueForMail) { + return function ( + string $domain, + string $errorMessage, + int $attempt, + ) use ($queueForMail) { + // Log error into console + Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); - // Send mail to administratore mail - $mail = new Mail(); - $mail - ->setType(MAIL_TYPE_CERTIFICATE) - ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) - ->setUrl('https://' . $domain) - ->setLocale(App::getEnv('_APP_LOCALE', 'en')) - ->setName('Appwrite Administrator') - ->setPayload([ - 'domain' => $domain, - 'error' => $errorMessage, - 'attempt' => $attempt - ]) - ->trigger(); -} + // Send mail to administratore mail + $queueForMail + ->setType(MAIL_TYPE_CERTIFICATE) + ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) + ->setUrl('https://' . $domain) + ->setLocale(App::getEnv('_APP_LOCALE', 'en')) + ->setName('Appwrite Administrator') + ->setPayload([ + 'domain' => $domain, + 'error' => $errorMessage, + 'attempt' => $attempt + ]) + ->trigger(); + }; +}, ['queueForMail']); /** * Update all existing domain documents so they have relation to correct certificate document. @@ -382,27 +418,30 @@ function notifyError(string $domain, string $errorMessage, int $attempt): void * @param string $certificateId ID of a new or updated certificate document * @param string $domain Domain that is affected by new certificate * @param Database $dbForConsole Database instance for console - * * @return void */ -function updateDomainDocuments(string $certificateId, string $domain, Database $dbForConsole): void -{ - $domains = $dbForConsole->find('domains', [ - Query::equal('domain', [$domain]), - Query::limit(1000), - ]); +Server::setResource('updateDomainDocuments', function () { + return function ( + string $certificateId, + string $domain, + Database $dbForConsole, + ) { + $domains = $dbForConsole->find('domains', [ + Query::equal('domain', [$domain]), + Query::limit(1000), + ]); - foreach ($domains as $domainDocument) { - $domainDocument->setAttribute('updated', DateTime::now()); - $domainDocument->setAttribute('certificateId', $certificateId); + foreach ($domains as $domainDocument) { + $domainDocument->setAttribute('updated', DateTime::now()); + $domainDocument->setAttribute('certificateId', $certificateId); + $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); - $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); - - if ($domainDocument->getAttribute('projectId')) { - $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + if ($domainDocument->getAttribute('projectId')) { + $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + } } - } -} + }; +}); $server->job() ->inject('message') @@ -416,10 +455,10 @@ $server->job() } $document = new Document($payload['domain'] ?? []); - $domain = new Domain($document->getAttribute('domain', '')); + $domain = new Domain($document->getAttribute('domain', '')); $skipRenewCheck = $payload['skipRenewCheck'] ?? false; - $execute($dbForConsole, $document, $domain, $skipRenewCheck); + $execute($document, $domain, $skipRenewCheck); }); $server->workerStart(); diff --git a/app/workers/databases.php b/app/workers/databases.php index b0087e2913..db2ff8ec84 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -1,6 +1,7 @@ $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); - /** - * Fetch attribute from the database, since with Resque float values are loosing informations. - */ - $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'attributeId' => $attribute->getId() + ]); - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); - $size = $attribute->getAttribute('size', 0); - $required = $attribute->getAttribute('required', false); - $default = $attribute->getAttribute('default', null); - $signed = $attribute->getAttribute('signed', true); - $array = $attribute->getAttribute('array', false); - $format = $attribute->getAttribute('format', ''); - $formatOptions = $attribute->getAttribute('formatOptions', []); - $filters = $attribute->getAttribute('filters', []); + /** + * Fetch attribute from the database, since with Resque float values are loosing informations. + */ + $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); - try { - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); - } - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); - } finally { - $target = Realtime::fromPayload( + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $type = $attribute->getAttribute('type', ''); + $size = $attribute->getAttribute('size', 0); + $required = $attribute->getAttribute('required', false); + $default = $attribute->getAttribute('default', null); + $signed = $attribute->getAttribute('signed', true); + $array = $attribute->getAttribute('array', false); + $format = $attribute->getAttribute('format', ''); + $formatOptions = $attribute->getAttribute('formatOptions', []); + $filters = $attribute->getAttribute('filters', []); + + try { + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create Attribute'); + } + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); + } finally { + $target = Realtime::fromPayload( // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project, - ); + event: $events[0], + payload: $attribute, + project: $project, + ); - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $project->getId(), - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); -} + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + }; +}, ['dbForConsole, dbForProject']); /** * @param Document $database * @param Document $collection * @param Document $attribute - * @param Database $dbForConsole * @param Document $project + * @throws \Utopia\Database\Exception\Authorization */ -function deleteDBAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole): void -{ - $dbForProject = getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ +Server::setResource('deleteDBAttribute', function (Database $dbForConsole, Database $dbForProject) { + return function ( + Document $project, + Document $database, + Document $collection, + Document $attribute, + ) use ( + $dbForConsole, + $dbForProject + ) { + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ 'databaseId' => $database->getId(), 'collectionId' => $collection->getId(), 'attributeId' => $attribute->getId() - ]); - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $status = $attribute->getAttribute('status', ''); + ]); + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $status = $attribute->getAttribute('status', ''); // possible states at this point: // - available: should not land in queue; controller flips these to 'deleting' @@ -118,90 +132,153 @@ function deleteDBAttribute(Document $database, Document $collection, Document $a // - deleting: was available, in deletion queue for first time // - failed: attribute was never created // - stuck: attribute was available but cannot be removed - try { - if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new Exception('Failed to delete Attribute'); - } - $dbForProject->deleteDocument('attributes', $attribute->getId()); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); - } finally { - $target = Realtime::fromPayload( + try { + if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new Exception('Failed to delete Attribute'); + } + $dbForProject->deleteDocument('attributes', $attribute->getId()); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); + } finally { + $target = Realtime::fromPayload( // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project - ); + event: $events[0], + payload: $attribute, + project: $project + ); - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $project->getId(), - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } // The underlying database removes/rebuilds indexes when attribute is removed // Update indexes table with changes /** @var Document[] $indexes */ - $indexes = $collection->getAttribute('indexes', []); + $indexes = $collection->getAttribute('indexes', []); - foreach ($indexes as $index) { - /** @var string[] $attributes */ - $attributes = $index->getAttribute('attributes'); - $lengths = $index->getAttribute('lengths'); - $orders = $index->getAttribute('orders'); + foreach ($indexes as $index) { + /** @var string[] $attributes */ + $attributes = $index->getAttribute('attributes'); + $lengths = $index->getAttribute('lengths'); + $orders = $index->getAttribute('orders'); - $found = \array_search($key, $attributes); + $found = \array_search($key, $attributes); - if ($found !== false) { - // If found, remove entry from attributes, lengths, and orders - // array_values wraps array_diff to reindex array keys - // when found attribute is removed from array - $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); - $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); - $orders = \array_values(\array_diff($orders, [$orders[$found]])); + if ($found !== false) { + // If found, remove entry from attributes, lengths, and orders + // array_values wraps array_diff to reindex array keys + // when found attribute is removed from array + $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); + $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); + $orders = \array_values(\array_diff($orders, [$orders[$found]])); - if (empty($attributes)) { - $dbForProject->deleteDocument('indexes', $index->getId()); - } else { - $index + if (empty($attributes)) { + $dbForProject->deleteDocument('indexes', $index->getId()); + } else { + $index ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); - // Check if an index exists with the same attributes and orders - $exists = false; - foreach ($indexes as $existing) { - if ( - $existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself - && $existing->getAttribute('attributes') === $index->getAttribute('attributes') - && $existing->getAttribute('orders') === $index->getAttribute('orders') - ) { - $exists = true; - break; + // Check if an index exists with the same attributes and orders + $exists = false; + foreach ($indexes as $existing) { + if ( + $existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself + && $existing->getAttribute('attributes') === $index->getAttribute('attributes') + && $existing->getAttribute('orders') === $index->getAttribute('orders') + ) { + $exists = true; + break; + } } - } - if ($exists) { // Delete the duplicate if created, else update in db - deleteIndex($database, $collection, $index, $project, $dbForConsole); - } else { - $dbForProject->updateDocument('indexes', $index->getId(), $index); + if ($exists) { // Delete the duplicate if created, else update in db + deleteIndex($database, $collection, $index, $project, $dbForConsole); + } else { + $dbForProject->updateDocument('indexes', $index->getId(), $index); + } } } } - } - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); -} + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + }; +}, ['dbForConsole, dbForProject']); + +/** + * @param Document $database + * @param Document $collection + * @param Document $index + * @param Document $project + */ +Server::setResource('createIndex', function (Database $dbForConsole, Database $dbForProject) { + return function ( + Document $project, + Document $database, + Document $collection, + Document $index, + ) use ( + $dbForConsole, + $dbForProject + ) { + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'indexId' => $index->getId() + ]); + $collectionId = $collection->getId(); + $key = $index->getAttribute('key', ''); + $type = $index->getAttribute('type', ''); + $attributes = $index->getAttribute('attributes', []); + $lengths = $index->getAttribute('lengths', []); + $orders = $index->getAttribute('orders', []); + + try { + if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + throw new Exception('Failed to create Index'); + } + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $index, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $index->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + }; +}, ['dbForConsole, dbForProject']); /** * @param Document $database @@ -210,111 +287,66 @@ function deleteDBAttribute(Document $database, Document $collection, Document $a * @param Database $dbForConsole * @param Document $project */ -function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole): void -{ - $dbForProject = getProjectDB($project); +Server::setResource('deleteIndex', function (Database $dbForConsole, Database $dbForProject) { + return function ( + Document $project, + Document $database, + Document $collection, + Document $index, + ) use ( + $dbForConsole, + $dbForProject + ) { - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); - $collectionId = $collection->getId(); - $key = $index->getAttribute('key', ''); - $type = $index->getAttribute('type', ''); - $attributes = $index->getAttribute('attributes', []); - $lengths = $index->getAttribute('lengths', []); - $orders = $index->getAttribute('orders', []); + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'indexId' => $index->getId() + ]); + $key = $index->getAttribute('key'); + $status = $index->getAttribute('status', ''); - try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { - throw new Exception('Failed to create Index'); - } - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); - } finally { - $target = Realtime::fromPayload( + try { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new Exception('Failed to delete index'); + } + $dbForProject->deleteDocument('indexes', $index->getId()); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); + } finally { + $target = Realtime::fromPayload( // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); + event: $events[0], + payload: $index, + project: $project + ); - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ + Realtime::send( + projectId: 'console', + payload: $index->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ 'projectId' => $project->getId(), 'databaseId' => $database->getId(), 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); -} - -/** - * @param Document $database - * @param Document $collection - * @param Document $index - * @param Database $dbForConsole - * @param Document $project - */ -function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole): void -{ - $dbForProject = getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); - $key = $index->getAttribute('key'); - $status = $index->getAttribute('status', ''); - - try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new Exception('Failed to delete index'); + ] + ); } - $dbForProject->deleteDocument('indexes', $index->getId()); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $project->getId(), - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); -} + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); + }; +}, ['dbForConsole, dbForProject']); $server->job() ->inject('message') - ->inject('dbForProject') - ->action(function (Message $message, Database $dbForProject) { + ->inject('createDBAttribute') + ->inject('deleteDBAttribute') + ->inject('createIndex') + ->inject('deleteIndex') + ->action(function (Message $message, callable $createDBAttribute, callable $deleteDBAttribute, callable $createIndex, callable $deleteIndex) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -335,24 +367,13 @@ $server->job() throw new Exception('Missing document'); } - switch (strval($type)) { - case DATABASE_TYPE_CREATE_ATTRIBUTE: - createDBAttribute($database, $collection, $document, $project, $dbForProject); - break; - case DATABASE_TYPE_DELETE_ATTRIBUTE: - deleteDBAttribute($database, $collection, $document, $project, $dbForProject); - break; - case DATABASE_TYPE_CREATE_INDEX: - createIndex($database, $collection, $document, $project, $dbForProject); - break; - case DATABASE_TYPE_DELETE_INDEX: - deleteIndex($database, $collection, $document, $project, $dbForProject); - break; - - default: - Console::error('No database operation for type: ' . $type); - break; - } + match (strval($type)) { + DATABASE_TYPE_CREATE_ATTRIBUTE => $createDBAttribute($database, $collection, $document, $project), + DATABASE_TYPE_DELETE_ATTRIBUTE => $deleteDBAttribute($database, $collection, $document, $project), + DATABASE_TYPE_CREATE_INDEX => $createIndex($database, $collection, $document, $project), + DATABASE_TYPE_DELETE_INDEX => $deleteIndex($database, $collection, $document, $project), + default => Console::error('No database operation for type: ' . $type), + }; }); $server->workerStart(); diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 0bf8829ee3..456d7f20b0 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -9,7 +9,7 @@ use Utopia\Cache\Cache; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Executor\Executor; +use Utopia\Queue\Server; use Utopia\Storage\Device\Local; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; @@ -22,710 +22,821 @@ use Utopia\Queue\Message; Authorization::disable(); Authorization::setDefaultStatus(false); -$server->job() - ->inject('message') - ->action(function (Message $message) { - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - $project = new Document($payload['project'] ?? []); - $type = $payload['type'] ?? ''; - switch (strval($type)) { - case DELETE_TYPE_DOCUMENT: - $document = new Document($payload['document'] ?? []); - - switch ($document->getCollection()) { - case DELETE_TYPE_DATABASES: - deleteDatabase($document, $project); - break; - case DELETE_TYPE_COLLECTIONS: - deleteCollection($document, $project); - break; - case DELETE_TYPE_PROJECTS: - deleteProject($document); - break; - case DELETE_TYPE_FUNCTIONS: - deleteFunction($document, $project); - break; - case DELETE_TYPE_DEPLOYMENTS: - deleteDeployment($document, $project); - break; - case DELETE_TYPE_USERS: - deleteUser($document, $project); - break; - case DELETE_TYPE_TEAMS: - deleteMemberships($document, $project); - break; - case DELETE_TYPE_BUCKETS: - deleteBucket($document, $project); - break; - default: - Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); - break; - } - break; - - case DELETE_TYPE_EXECUTIONS: - deleteExecutionLogs($payload['datetime']); - break; - - case DELETE_TYPE_AUDIT: - $datetime = $payload['datetime'] ?? null; - if (!empty($datetime)) { - deleteAuditLogs($datetime); - } - - $document = new Document($payload['document'] ?? []); - - if (!$document->isEmpty()) { - deleteAuditLogsByResource('document/' . $document->getId(), $project); - } - - break; - - case DELETE_TYPE_ABUSE: - deleteAbuseLogs($payload['datetime']); - break; - - case DELETE_TYPE_REALTIME: - deleteRealtimeUsage($payload['datetime']); - break; - - case DELETE_TYPE_SESSIONS: - deleteExpiredSessions(); - break; - - case DELETE_TYPE_CERTIFICATES: - $document = new Document($payload['document']); - deleteCertificates($document); - break; - - case DELETE_TYPE_USAGE: - deleteUsageStats($payload['hourlyUsageRetentionDatetime']); - break; - - case DELETE_TYPE_CACHE_BY_RESOURCE: - deleteCacheByResource($payload['resource']); - break; - case DELETE_TYPE_CACHE_BY_TIMESTAMP: - deleteCacheByDate($payload); - break; - case DELETE_TYPE_SCHEDULES: - deleteSchedules($payload['datetime']); - break; - default: - Console::error('No delete operation for type: ' . $type); - break; - } - }); - - /** * @throws Exception */ -function deleteSchedules(string $datetime): void -{ - listByGroup( - 'schedules', - [ - Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), - Query::equal('resourceType', ['function']), - Query::lessThanEqual('resourceUpdatedAt', $datetime), - Query::equal('active', [false]), - ], - getConsoleDB(), - function (Document $document) { - $project = getConsoleDB()->getDocument('projects', $document->getAttribute('projectId')); +Server::setResource('deleteSchedules', function ($deleteCacheFiles, $dbForConsole, $getProjectDB, $listByGroup) { + return function (string $datetime) use ($deleteCacheFiles, $dbForConsole, $getProjectDB, $listByGroup) { - if ($project->isEmpty()) { - Console::warning('Unable to delete schedule for function ' . $document->getAttribute('resourceId')); - return; - } + $listByGroup( + 'schedules', + [ + Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), + Query::equal('resourceType', ['function']), + Query::lessThanEqual('resourceUpdatedAt', $datetime), + Query::equal('active', [false]), + ], + $dbForConsole, + function (Document $document) use ($getProjectDB, $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $document->getAttribute('projectId')); - $function = getProjectDB($project)->getDocument('functions', $document->getAttribute('resourceId')); + if ($project->isEmpty()) { + Console::warning('Unable to delete schedule for function ' . $document->getAttribute('resourceId')); + return; + } - if ($function->isEmpty()) { - getConsoleDB()->deleteDocument('schedules', $document->getId()); - Console::success('Deleting schedule for function ' . $document->getAttribute('resourceId')); - } - } - ); -} + $function = $getProjectDB($project)->getDocument('functions', $document->getAttribute('resourceId')); - /** - * @param string $resource - */ -function deleteCacheByResource(string $resource): void -{ - deleteCacheFiles([ - Query::equal('resource', [$resource]), - ]); -} - -function deleteCacheByDate($payload): void -{ - deleteCacheFiles([ - Query::lessThan('accessedAt', $payload['datetime']), - ]); -} - -function deleteCacheFiles($query): void -{ - deleteForProjectIds(function (Document $project) use ($query) { - - $projectId = $project->getId(); - $dbForProject = getProjectDB($project); - $cache = new Cache( - new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) - ); - - deleteByGroup( - 'cache', - $query, - $dbForProject, - function (Document $document) use ($cache, $projectId) { - $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); - - if ($cache->purge($document->getId())) { - Console::success('Deleting cache file: ' . $path); - } else { - Console::error('Failed to delete cache file: ' . $path); + if ($function->isEmpty()) { + $dbForConsole->deleteDocument('schedules', $document->getId()); + Console::success('Deleting schedule for function ' . $document->getAttribute('resourceId')); } } ); - }); -} + }; +}, ['deleteCacheFiles', 'dbForConsole', 'getProjectDB', 'listByGroup']); - /** +Server::setResource('deleteCacheByResource', function ($deleteCacheFiles) { + return function (array $payload, Document $project) use ($deleteCacheFiles) { + $deleteCacheFiles([ + Query::equal('resource', [$payload['resource']]), + ], $project); + }; +}, ['deleteCacheFiles']); + +Server::setResource('deleteCacheByDate', function ($deleteCacheFiles) { + return function (array $payload, Document $project) use ($deleteCacheFiles) { + $deleteCacheFiles([ + Query::lessThan('accessedAt', $payload['datetime']), + ], $project); + }; +}, ['deleteCacheFiles']); + +Server::setResource('deleteCacheFiles', function ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + return function (string $query, Document $project) use ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + + $deleteForProjectIds(function () use ($deleteByGroup, $getProjectDB, $query, $project) { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $cache = new Cache( + new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) + ); + + $deleteByGroup( + 'cache', + $query, + $dbForProject, + function (Document $document) use ($cache, $projectId) { + $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); + + if ($cache->purge($document->getId())) { + Console::success('Deleting cache file: ' . $path); + } else { + Console::error('Failed to delete cache file: ' . $path); + } + } + ); + }); + }; +}, ['getProjectDB', 'deleteForProjectIds', 'deleteByGroup']); + + +/** * @param Document $document database document * @param Document $projectId */ -function deleteDatabase(Document $document, Document $project): void -{ - $databaseId = $document->getId(); - $projectId = $project->getId(); + Server::setResource('deleteDatabase', function ($getProjectDB, $deleteAuditLogsByResource, $deleteByGroup, $deleteCollection) { + return function (Document $document, Document $project) use ($getProjectDB, $deleteAuditLogsByResource, $deleteByGroup, $deleteCollection) { - $dbForProject = getProjectDB($project); + $databaseId = $document->getId(); + $dbForProject = getProjectDB($project); - deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($project) { - deleteCollection($document, $project); - }); + $deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($deleteCollection, $project) { + $deleteCollection($document, $project); + }); - $dbForProject->deleteCollection('database_' . $document->getInternalId()); + $dbForProject->deleteCollection('database_' . $document->getInternalId()); - deleteAuditLogsByResource('database/' . $databaseId, $project); -} + $deleteAuditLogsByResource('database/' . $databaseId, $project); + }; + }, ['getProjectDB', 'deleteAuditLogsByResource', 'deleteByGroup', 'deleteCollection']); /** * @param Document $document teams document * @param Document $project */ -function deleteCollection(Document $document, Document $project): void -{ - $collectionId = $document->getId(); - $databaseId = $document->getAttribute('databaseId'); - $databaseInternalId = $document->getAttribute('databaseInternalId'); + Server::setResource('deleteCollection', function ($getProjectDB, $deleteAuditLogsByResource, $deleteByGroup) { + return function (Document $document, Document $project) use ($getProjectDB, $deleteAuditLogsByResource, $deleteByGroup) { - $dbForProject = getProjectDB($project); + $collectionId = $document->getId(); + $databaseId = $document->getAttribute('databaseId'); + $databaseInternalId = $document->getAttribute('databaseInternalId'); - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); + $dbForProject = getProjectDB($project); - deleteByGroup('attributes', [ - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]) - ], $dbForProject); + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); - deleteByGroup('indexes', [ - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]) - ], $dbForProject); + $deleteByGroup('attributes', [ + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$collectionId]) + ], $dbForProject); - deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project); -} + $deleteByGroup('indexes', [ + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$collectionId]) + ], $dbForProject); - /** - * @param string $hourlyUsageRetentionDatetime - */ -function deleteUsageStats(string $hourlyUsageRetentionDatetime) -{ - deleteForProjectIds(function (Document $project) use ($hourlyUsageRetentionDatetime) { - $dbForProject = getProjectDB($project); - // Delete Usage stats - deleteByGroup('stats', [ - Query::lessThan('time', $hourlyUsageRetentionDatetime), - Query::equal('period', ['1h']), - ], $dbForProject); - }); -} + $deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project); + }; + }, ['getProjectDB', 'deleteAuditLogsByResource', 'deleteByGroup']); - /** - * @param Document $document teams document - * @param Document $project - */ -function deleteMemberships(Document $document, Document $project): void -{ - $teamId = $document->getAttribute('teamId', ''); +/** + * @param string $hourlyUsageRetentionDatetime + */ + Server::setResource('deleteUsageStats', function ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + return function (string $hourlyUsageRetentionDatetime) use ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { - // Delete Memberships - deleteByGroup('memberships', [ - Query::equal('teamId', [$teamId]) - ], getProjectDB($project)); -} + $deleteForProjectIds(function (Document $project) use ($getProjectDB, $deleteByGroup, $hourlyUsageRetentionDatetime) { + $dbForProject = $getProjectDB($project); + // Delete Usage stats + $deleteByGroup('stats', [ + Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::equal('period', ['1h']), + ], $dbForProject); + }); + }; + }, ['getProjectDB', 'deleteForProjectIds', 'deleteByGroup']); - /** - * @param Document $document project document - */ -function deleteProject(Document $document): void -{ - $projectId = $document->getId(); +/** + * @param Document $document teams document + * @param Document $project + */ + Server::setResource('deleteMemberships', function ($getProjectDB, $deleteByGroup) { + return function (Document $document, Document $project) use ($getProjectDB, $deleteByGroup) { - // Delete all DBs - getProjectDB($document)->delete($projectId); + $teamId = $document->getAttribute('teamId', ''); - // Delete all storage directories - $uploads = getFilesDevice($document->getId()); - $cache = new Local(APP_STORAGE_CACHE . '/app-' . $document->getId()); + // Delete Memberships + $deleteByGroup('memberships', [ + Query::equal('teamId', [$teamId]) + ], getProjectDB($project)); + }; + }, ['getProjectDB', 'deleteByGroup']); - $uploads->delete($uploads->getRoot(), true); - $cache->delete($cache->getRoot(), true); -} - /** - * @param Document $document user document - * @param Document $project - */ -function deleteUser(Document $document, Document $project): void -{ - $userId = $document->getId(); +/** + * @param Document $document project document + */ + Server::setResource('deleteProject', function ($getProjectDB, $getFilesDevice) { + return function (Document $document) use ($getProjectDB, $getFilesDevice) { - $dbForProject = getProjectDB($project); + $projectId = $document->getId(); - // Delete all sessions of this user from the sessions table and update the sessions field of the user record - deleteByGroup('sessions', [ - Query::equal('userId', [$userId]) - ], $dbForProject); + // Delete all DBs + $getProjectDB($document)->delete($projectId); - $dbForProject->deleteCachedDocument('users', $userId); + // Delete all storage directories + $uploads = $getFilesDevice($document->getId()); + $cache = new Local(APP_STORAGE_CACHE . '/app-' . $document->getId()); - // Delete Memberships and decrement team membership counts - deleteByGroup('memberships', [ - Query::equal('userId', [$userId]) - ], $dbForProject, function (Document $document) use ($dbForProject) { - if ($document->getAttribute('confirm')) { // Count only confirmed members - $teamId = $document->getAttribute('teamId'); - $team = $dbForProject->getDocument('teams', $teamId); - if (!$team->isEmpty()) { - $team = $dbForProject->updateDocument( - 'teams', - $teamId, - // Ensure that total >= 0 - $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)) - ); + $uploads->delete($uploads->getRoot(), true); + $cache->delete($cache->getRoot(), true); + }; + }, ['getProjectDB', 'getFilesDevice']); + +/** + * @param Document $document user document + * @param Document $project + */ + Server::setResource('deleteUser', function ($getProjectDB, $deleteByGroup) { + return function (Document $document, Document $project) use ($getProjectDB, $deleteByGroup) { + + $userId = $document->getId(); + $dbForProject = getProjectDB($project); + + // Delete all sessions of this user from the sessions table and update the sessions field of the user record + $deleteByGroup('sessions', [ + Query::equal('userId', [$userId]) + ], $dbForProject); + + $dbForProject->deleteCachedDocument('users', $userId); + + // Delete Memberships and decrement team membership counts + $deleteByGroup('memberships', [ + Query::equal('userId', [$userId]) + ], $dbForProject, function (Document $document) use ($dbForProject) { + if ($document->getAttribute('confirm')) { // Count only confirmed members + $teamId = $document->getAttribute('teamId'); + $team = $dbForProject->getDocument('teams', $teamId); + if (!$team->isEmpty()) { + $team = $dbForProject->updateDocument( + 'teams', + $teamId, + // Ensure that total >= 0 + $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)) + ); + } + } + }); + + // Delete tokens + $deleteByGroup('tokens', [ + Query::equal('userId', [$userId]) + ], $dbForProject); + }; + }, ['getProjectDB', 'deleteByGroup']); + +/** + * @param string $datetime + */ + Server::setResource('deleteExecutionLogs', function ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + return function (string $datetime) use ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + + $deleteForProjectIds(function (Document $project) use ($deleteByGroup, $datetime) { + $dbForProject = getProjectDB($project); + // Delete Executions + $deleteByGroup('executions', [ + Query::lessThan('$createdAt', $datetime) + ], $dbForProject); + }); + }; + }, ['getProjectDB', 'deleteForProjectIds', 'deleteByGroup']); + + + Server::setResource('deleteExpiredSessions', function ($getProjectDB, $deleteForProjectIds, $deleteByGroup, $dbForConsole) { + return function () use ($getProjectDB, $deleteForProjectIds, $deleteByGroup, $dbForConsole) { + + $deleteForProjectIds(function (Document $project) use ($deleteByGroup, $getProjectDB, $dbForConsole) { + $dbForProject = $getProjectDB($project); + + $project = $dbForConsole->getDocument('projects', $project->getId()); + $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); + + // Delete Sessions + $deleteByGroup('sessions', [ + Query::lessThan('$createdAt', $expired) + ], $dbForProject); + }); + }; + }, ['getProjectDB', 'deleteForProjectIds', 'deleteByGroup', 'dbForConsole']); + + +/** + * @param string $datetime + */ + Server::setResource('deleteRealtimeUsage', function ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + return function (string $datetime) use ($getProjectDB, $deleteForProjectIds, $deleteByGroup) { + $deleteForProjectIds(function (Document $project) use ($deleteByGroup, $getProjectDB, $datetime) { + $dbForProject = $getProjectDB($project); + // Delete Dead Realtime Logs + $deleteByGroup('realtime', [ + Query::lessThan('timestamp', $datetime) + ], $dbForProject); + }); + }; + }, ['getProjectDB', 'deleteForProjectIds', 'deleteByGroup']); + +/** + * @param string $datetime + * @throws Exception + */ + Server::setResource('deleteAbuseLogs', function ($getProjectDB, $deleteForProjectIds) { + return function (string $datetime) use ($getProjectDB, $deleteForProjectIds) { + + if (empty($datetime)) { + throw new Exception('Failed to delete audit logs. No datetime provided'); } - } - }); - // Delete tokens - deleteByGroup('tokens', [ - Query::equal('userId', [$userId]) - ], $dbForProject); -} + $deleteForProjectIds(function (Document $project) use ($datetime) { + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $timeLimit = new TimeLimit("", 0, 1, $dbForProject); + $abuse = new Abuse($timeLimit); + $status = $abuse->cleanup($datetime); + if (!$status) { + throw new Exception('Failed to delete Abuse logs for project ' . $projectId); + } + }); + }; + }, ['getProjectDB', 'deleteForProjectIds']); - /** - * @param string $datetime - */ -function deleteExecutionLogs(string $datetime): void -{ - deleteForProjectIds(function (Document $project) use ($datetime) { - $dbForProject = getProjectDB($project); - // Delete Executions - deleteByGroup('executions', [ - Query::lessThan('$createdAt', $datetime) - ], $dbForProject); - }); -} +/** + * @param string $datetime + * @throws Exception + */ + Server::setResource('deleteAuditLogs', function ($getProjectDB, $deleteForProjectIds) { + return function (string $datetime) use ($getProjectDB, $deleteForProjectIds) { -function deleteExpiredSessions(): void -{ - $consoleDB = getConsoleDB(); + if (empty($datetime)) { + throw new Exception('Failed to delete audit logs. No datetime provided'); + } - deleteForProjectIds(function (Document $project) use ($consoleDB) { - $dbForProject = getProjectDB($project); + $deleteForProjectIds(function (Document $project) use ($datetime) { + $projectId = $project->getId(); + $dbForProject = getProjectDB($project); + $audit = new Audit($dbForProject); + $status = $audit->cleanup($datetime); + if (!$status) { + throw new Exception('Failed to delete Audit logs for project' . $projectId); + } + }); + }; + }, ['getProjectDB', 'deleteForProjectIds']); - $project = $consoleDB->getDocument('projects', $project->getId()); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); +/** + * @param string $resource + * @param Document $project + */ + Server::setResource('deleteAuditLogsByResource', function ($getProjectDB, $deleteByGroup) { + return function (string $resource, Document $project) use ($getProjectDB, $deleteByGroup) { + $dbForProject = getProjectDB($project); - // Delete Sessions - deleteByGroup('sessions', [ - Query::lessThan('$createdAt', $expired) - ], $dbForProject); - }); -} + $deleteByGroup(Audit::COLLECTION, [ + Query::equal('resource', [$resource]) + ], $dbForProject); + }; + }, ['getProjectDB', 'deleteByGroup']); - /** - * @param string $datetime - */ -function deleteRealtimeUsage(string $datetime): void -{ - deleteForProjectIds(function (Document $project) use ($datetime) { - $dbForProject = getProjectDB($project); - // Delete Dead Realtime Logs - deleteByGroup('realtime', [ - Query::lessThan('timestamp', $datetime) - ], $dbForProject); - }); -} +/** + * @param Document $document function document + * @param Document $project + */ + Server::setResource('deleteFunction', function ($getProjectDB, $deleteByGroup, $getFunctionsDevice, $getBuildsDevice) { + return function (Document $document, Document $project) use ($getProjectDB, $deleteByGroup, $getFunctionsDevice, $getBuildsDevice) { - /** - * @param string $datetime - * @throws Exception - */ -function deleteAbuseLogs(string $datetime): void -{ - if (empty($datetime)) { - throw new Exception('Failed to delete audit logs. No datetime provided'); - } + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $functionId = $document->getId(); - deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = getProjectDB($project); - $timeLimit = new TimeLimit("", 0, 1, $dbForProject); - $abuse = new Abuse($timeLimit); - $status = $abuse->cleanup($datetime); - if (!$status) { - throw new Exception('Failed to delete Abuse logs for project ' . $projectId); - } - }); -} + /** + * Delete Variables + */ + Console::info("Deleting variables for function " . $functionId); + $deleteByGroup('variables', [ + Query::equal('functionId', [$functionId]) + ], $dbForProject); - /** - * @param string $datetime - * @throws Exception - */ -function deleteAuditLogs(string $datetime): void -{ - if (empty($datetime)) { - throw new Exception('Failed to delete audit logs. No datetime provided'); - } + /** + * Delete Deployments + */ + Console::info("Deleting deployments for function " . $functionId); + $storageFunctions = $getFunctionsDevice($projectId); + $deploymentIds = []; + $deleteByGroup('deployments', [ + Query::equal('resourceId', [$functionId]) + ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) { + $deploymentIds[] = $document->getId(); + if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); + } else { + Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); + } + }); - deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = getProjectDB($project); - $audit = new Audit($dbForProject); - $status = $audit->cleanup($datetime); - if (!$status) { - throw new Exception('Failed to delete Audit logs for project' . $projectId); - } - }); -} + /** + * Delete builds + */ + Console::info("Deleting builds for function " . $functionId); + $storageBuilds = $getBuildsDevice($projectId); + foreach ($deploymentIds as $deploymentId) { + $deleteByGroup('builds', [ + Query::equal('deploymentId', [$deploymentId]) + ], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) { + if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('path', '')); + } else { + Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); + } + }); + } - /** - * @param string $resource - * @param Document $project - */ -function deleteAuditLogsByResource(string $resource, Document $project): void -{ - $dbForProject = getProjectDB($project); + /** + * Delete Executions + */ + Console::info("Deleting executions for function " . $functionId); + $deleteByGroup('executions', [ + Query::equal('functionId', [$functionId]) + ], $dbForProject); - deleteByGroup(Audit::COLLECTION, [ - Query::equal('resource', [$resource]) - ], $dbForProject); -} + // TODO: Request executor to delete runtime + }; + }, ['getProjectDB' ,'$deleteByGroup', '$getFunctionsDevice', '$getBuildsDevice']); - /** - * @param Document $document function document - * @param Document $project - */ -function deleteFunction(Document $document, Document $project): void -{ - $projectId = $project->getId(); - $dbForProject = getProjectDB($project); - $functionId = $document->getId(); +/** + * @param Document $document deployment document + * @param Document $project + */ + Server::setResource('deleteDeployment', function ($getProjectDB, $getBuildsDevice, $getFunctionsDevice, $deleteByGroup) { + return function (Document $document, Document $project) use ($getProjectDB, $getBuildsDevice, $getFunctionsDevice, $deleteByGroup) { - /** - * Delete Variables - */ - Console::info("Deleting variables for function " . $functionId); - deleteByGroup('variables', [ - Query::equal('functionId', [$functionId]) - ], $dbForProject); + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $deploymentId = $document->getId(); + $functionId = $document->getAttribute('resourceId'); - /** - * Delete Deployments - */ - Console::info("Deleting deployments for function " . $functionId); - $storageFunctions = getFunctionsDevice($projectId); - $deploymentIds = []; - deleteByGroup('deployments', [ - Query::equal('resourceId', [$functionId]) - ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) { - $deploymentIds[] = $document->getId(); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } - }); - - /** - * Delete builds - */ - Console::info("Deleting builds for function " . $functionId); - $storageBuilds = getBuildsDevice($projectId); - foreach ($deploymentIds as $deploymentId) { - deleteByGroup('builds', [ - Query::equal('deploymentId', [$deploymentId]) - ], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) { - if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('path', '')); + /** + * Delete deployment files + */ + Console::info("Deleting deployment files for deployment " . $deploymentId); + $storageFunctions = $getFunctionsDevice($projectId); + if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); + Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); } - }); - } - /** - * Delete Executions - */ - Console::info("Deleting executions for function " . $functionId); - deleteByGroup('executions', [ - Query::equal('functionId', [$functionId]) - ], $dbForProject); + /** + * Delete builds + */ + Console::info("Deleting builds for deployment " . $deploymentId); + $storageBuilds = $getBuildsDevice($projectId); + $deleteByGroup('builds', [ + Query::equal('deploymentId', [$deploymentId]) + ], $dbForProject, function (Document $document) use ($storageBuilds) { + if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('path', '')); + } else { + Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); + } + }); - // TODO: Request executor to delete runtime -} + // TODO: Request executor to delete runtime + }; + }, ['getProjectDB' ,'getBuildsDevice', 'getFunctionsDevice', 'deleteByGroup']); - /** - * @param Document $document deployment document - * @param Document $project - */ -function deleteDeployment(Document $document, Document $project): void -{ - $projectId = $project->getId(); - $dbForProject = getProjectDB($project); - $deploymentId = $document->getId(); - $functionId = $document->getAttribute('resourceId'); - /** - * Delete deployment files - */ - Console::info("Deleting deployment files for deployment " . $deploymentId); - $storageFunctions = getFunctionsDevice($projectId); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } +/** + * @param Document $document to be deleted + * @param Database $database to delete it from + * @param callable $callback to perform after document is deleted + * @return bool + */ + Server::setResource('deleteById', function () { + return function ( + Document $document, + Database $database, + callable $callback = null + ) { + if ($database->deleteDocument($document->getCollection(), $document->getId())) { + Console::success('Deleted document "' . $document->getId() . '" successfully'); - /** - * Delete builds - */ - Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = getBuildsDevice($projectId); - deleteByGroup('builds', [ - Query::equal('deploymentId', [$deploymentId]) - ], $dbForProject, function (Document $document) use ($storageBuilds) { - if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); - } + if (is_callable($callback)) { + $callback($document); + } + + return true; + } else { + Console::error('Failed to delete document: ' . $document->getId()); + return false; + } + }; }); - // TODO: Request executor to delete runtime -} +/** + * @param callable $callback + */ + Server::setResource('deleteForProjectIds', function () { + return function ( + callable $callback = null + ) { + // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document + $count = 0; + $chunk = 0; + $limit = 50; + $projects = []; + $sum = $limit; - /** - * @param Document $document to be deleted - * @param Database $database to delete it from - * @param callable $callback to perform after document is deleted - * - * @return bool - */ -function deleteById(Document $document, Database $database, callable $callback = null): bool -{ - if ($database->deleteDocument($document->getCollection(), $document->getId())) { - Console::success('Deleted document "' . $document->getId() . '" successfully'); + $executionStart = \microtime(true); - if (is_callable($callback)) { - $callback($document); - } + while ($sum === $limit) { + $projects = getConsoleDB()->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); - return true; - } else { - Console::error('Failed to delete document: ' . $document->getId()); - return false; - } -} + $chunk++; - /** - * @param callable $callback - */ -function deleteForProjectIds(callable $callback): void -{ - // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document - $count = 0; - $chunk = 0; - $limit = 50; - $projects = []; - $sum = $limit; + /** @var string[] $projectIds */ + $sum = count($projects); - $executionStart = \microtime(true); - - while ($sum === $limit) { - $projects = getConsoleDB()->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); - - $chunk++; - - /** @var string[] $projectIds */ - $sum = count($projects); - - Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects'); - foreach ($projects as $project) { - $callback($project); - $count++; - } - } - - $executionEnd = \microtime(true); - Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); -} - - /** - * @param string $collection collectionID - * @param Query[] $queries - * @param Database $database - * @param callable $callback - */ -function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void -{ - $count = 0; - $chunk = 0; - $limit = 50; - $results = []; - $sum = $limit; - - $executionStart = \microtime(true); - - while ($sum === $limit) { - $chunk++; - - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); - - $sum = count($results); - - Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); - - foreach ($results as $document) { - deleteById($document, $database, $callback); - $count++; - } - } - - $executionEnd = \microtime(true); - - Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); -} - - /** - * @param string $collection collectionID - * @param Query[] $queries - * @param Database $database - * @param callable $callback - */ -function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void -{ - $count = 0; - $chunk = 0; - $limit = 50; - $results = []; - $sum = $limit; - - $executionStart = \microtime(true); - - while ($sum === $limit) { - $chunk++; - - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); - - $sum = count($results); - - foreach ($results as $document) { - if (is_callable($callback)) { - $callback($document); + Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects'); + foreach ($projects as $project) { + $callback($project); + $count++; + } } - $count++; - } - } + $executionEnd = \microtime(true); + Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); + }; + }); - $executionEnd = \microtime(true); +/** + * @param string $collection collectionID + * @param Query[] $queries + * @param Database $database + * @param callable $callback + */ + Server::setResource('deleteByGroup', function () { + return function ( + string $collection, + array $queries, + Database $database, + callable $callback = null + ) { + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; - Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); -} + $executionStart = \microtime(true); - /** - * @param Document $document certificates document - */ -function deleteCertificates(Document $document): void -{ - $consoleDB = getConsoleDB(); + while ($sum === $limit) { + $chunk++; - // If domain has certificate generated - if (isset($document['certificateId'])) { - $domainUsingCertificate = $consoleDB->findOne('domains', [ - Query::equal('certificateId', [$document['certificateId']]) + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + + $sum = count($results); + + Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); + + foreach ($results as $document) { + deleteById($document, $database, $callback); + $count++; + } + } + + $executionEnd = \microtime(true); + Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + }; + }); + +/** + * @param string $collection collectionID + * @param Query[] $queries + * @param Database $database + * @param callable|null $callback + * @throws Exception + */ + Server::setResource('listByGroup', function () { + return function ( + string $collection, + array $queries, + Database $database, + callable $callback = null + ) { + + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + $sum = count($results); + + foreach ($results as $document) { + if (is_callable($callback)) { + $callback($document); + } + + $count++; + } + } + $executionEnd = \microtime(true); + Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + }; + }); + +/** + * @param Document $document certificates document + */ + Server::setResource('deleteCertificates', function (Database $dbForConsole) { + return function (Document $document) use ($dbForConsole) { + + // If domain has certificate generated + if (isset($document['certificateId'])) { + $domainUsingCertificate = $dbForConsole->findOne('domains', [ + Query::equal('certificateId', [$document['certificateId']]) + ]); + + if (!$domainUsingCertificate) { + $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); + if ($mainDomain === $document->getAttribute('domain')) { + $domainUsingCertificate = $mainDomain; + } + } + + // If certificate is still used by some domain, mark we can't delete. + // Current domain should not be found, because we only have copy. Original domain is already deleted from database. + if ($domainUsingCertificate) { + Console::warning("Skipping certificate deletion, because a domain is still using it."); + return; + } + } + + $domain = $document->getAttribute('domain'); + $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; + $checkTraversal = realpath($directory) === $directory; + + if ($domain && $checkTraversal && is_dir($directory)) { + // Delete certificate document, so Appwrite is aware of change + if (isset($document['certificateId'])) { + $dbForConsole->deleteDocument('certificates', $document['certificateId']); + } + + // Delete files, so Traefik is aware of change + array_map('unlink', glob($directory . '/*.*')); + rmdir($directory); + Console::info("Deleted certificate files for {$domain}"); + } else { + Console::info("No certificate files found for {$domain}"); + } + }; + }, ['dbForConsole']); + + Server::setResource('deleteBucket', function (callable $getProjectDB, callable $getFilesDevice) { + return function ( + Document $document, + Document $project, + ) use ( + $getProjectDB, + $getFilesDevice + ) { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); + + $device = $getFilesDevice($projectId); + + $device->deletePath($document->getId()); + }; + }, ['getProjectDB', 'getFilesDevice']); + + $server->job() + ->inject('message') + ->inject('deleteDatabase') + ->inject('deleteCollection') + ->inject('deleteProject') + ->inject('deleteFunction') + ->inject('deleteDeployment') + ->inject('deleteUser') + ->inject('deleteMemberships') + ->inject('deleteBucket') + ->inject('deleteExecutionLogs') + ->inject('deleteAuditLogs') + ->inject('deleteAuditLogsByResource') + ->inject('deleteAbuseLogs') + ->inject('deleteRealtimeUsage') + ->inject('deleteExpiredSessions') + ->inject('deleteCertificates') + ->inject('deleteUsageStats') + ->inject('deleteCacheByResource') + ->inject('deleteCacheByDate') + ->inject('deleteSchedules') + ->action(function ( + Message $message, + $deleteDatabase, + $deleteCollection, + $deleteProject, + $deleteFunction, + $deleteDeployment, + $deleteUser, + $deleteMemberships, + $deleteBucket, + $deleteExecutionLogs, + $deleteAuditLogs, + $deleteAuditLogsByResource, + $deleteAbuseLogs, + $deleteRealtimeUsage, + $deleteExpiredSessions, + $deleteCertificates, + $deleteUsageStats, + $deleteCacheByResource, + $deleteCacheByDate, + $deleteSchedules + ) { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $project = new Document($payload['project'] ?? []); + $type = $payload['type'] ?? ''; + switch (strval($type)) { + case DELETE_TYPE_DOCUMENT: + $document = new Document($payload['document'] ?? []); + + switch ($document->getCollection()) { + case DELETE_TYPE_DATABASES: + $deleteDatabase($document, $project); + break; + case DELETE_TYPE_COLLECTIONS: + $deleteCollection($document, $project); + break; + case DELETE_TYPE_PROJECTS: + $deleteProject($document); + break; + case DELETE_TYPE_FUNCTIONS: + $deleteFunction($document, $project); + break; + case DELETE_TYPE_DEPLOYMENTS: + $deleteDeployment($document, $project); + break; + case DELETE_TYPE_USERS: + $deleteUser($document, $project); + break; + case DELETE_TYPE_TEAMS: + $deleteMemberships($document, $project); + break; + case DELETE_TYPE_BUCKETS: + $deleteBucket($document, $project); + break; + default: + Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); + break; + } + break; + + case DELETE_TYPE_EXECUTIONS: + $deleteExecutionLogs($payload['datetime']); + break; + + case DELETE_TYPE_AUDIT: + $datetime = $payload['datetime'] ?? null; + if (!empty($datetime)) { + $deleteAuditLogs($datetime); + } + + $document = new Document($payload['document'] ?? []); + + if (!$document->isEmpty()) { + $deleteAuditLogsByResource('document/' . $document->getId(), $project); + } + + break; + + case DELETE_TYPE_ABUSE: + $deleteAbuseLogs($payload['datetime']); + break; + + case DELETE_TYPE_REALTIME: + $deleteRealtimeUsage($payload['datetime']); + break; + + case DELETE_TYPE_SESSIONS: + $deleteExpiredSessions(); + break; + + case DELETE_TYPE_CERTIFICATES: + $document = new Document($payload['document']); + $deleteCertificates($document); + break; + + case DELETE_TYPE_USAGE: + $deleteUsageStats($payload['hourlyUsageRetentionDatetime']); + break; + + case DELETE_TYPE_CACHE_BY_RESOURCE: + $deleteCacheByResource($payload, $project); + break; + case DELETE_TYPE_CACHE_BY_TIMESTAMP: + $deleteCacheByDate($payload, $project); + break; + case DELETE_TYPE_SCHEDULES: + $deleteSchedules($payload['datetime']); + break; + default: + Console::error('No delete operation for type: ' . $type); + break; + }; + }, [ + 'deleteDatabase', + 'deleteCollection', + 'deleteProject', + 'deleteFunction', + 'deleteDeployment', + 'deleteUser', + 'deleteMemberships', + 'deleteBucket', + 'deleteExecutionLogs', + 'deleteAuditLogs', + 'deleteAuditLogsByResource', + 'deleteAbuseLogs', + 'deleteRealtimeUsage', + 'deleteExpiredSessions', + 'deleteCertificates', + 'deleteUsageStats', + 'deleteCacheByResource', + 'deleteCacheByDate', + 'deleteSchedules' ]); - if (!$domainUsingCertificate) { - $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); - if ($mainDomain === $document->getAttribute('domain')) { - $domainUsingCertificate = $mainDomain; - } - } - - // If certificate is still used by some domain, mark we can't delete. - // Current domain should not be found, because we only have copy. Original domain is already deleted from database. - if ($domainUsingCertificate) { - Console::warning("Skipping certificate deletion, because a domain is still using it."); - return; - } - } - - $domain = $document->getAttribute('domain'); - $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; - $checkTraversal = realpath($directory) === $directory; - - if ($domain && $checkTraversal && is_dir($directory)) { - // Delete certificate document, so Appwrite is aware of change - if (isset($document['certificateId'])) { - $consoleDB->deleteDocument('certificates', $document['certificateId']); - } - - // Delete files, so Traefik is aware of change - array_map('unlink', glob($directory . '/*.*')); - rmdir($directory); - Console::info("Deleted certificate files for {$domain}"); - } else { - Console::info("No certificate files found for {$domain}"); - } -} - -function deleteBucket(Document $document, Document $project) -{ - $projectId = $project->getId(); - $dbForProject = getProjectDB($project); - $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); - - $device = getFilesDevice($projectId); - - $device->deletePath($document->getId()); -} - -$server->workerStart(); -$server->start(); + $server->workerStart(); + $server->start(); diff --git a/app/workers/functions.php b/app/workers/functions.php index 5ae5f852d2..96bdf65671 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -2,52 +2,54 @@ require_once __DIR__ . '/../worker.php'; +use Appwrite\Event\Usage; use Utopia\Queue\Message; use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Messaging\Adapter\Realtime; -use Appwrite\Usage\Stats; use Appwrite\Utopia\Response\Model\Execution; -use Domnikl\Statsd\Client; use Executor\Executor; use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\ID; -use Utopia\Database\Permission; +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; use Utopia\Database\Query; -use Utopia\Database\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Queue\Connection; use Utopia\Queue\Server; +use Utopia\Database\Helpers\Role; + Authorization::disable(); Authorization::setDefaultStatus(false); -Server::setResource('execute', function () { +Server::setResource('execute', function (Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage) { return function ( - Client $statsd, - Database $dbForProject, Document $project, Document $function, - Func $queueForFunctions, string $trigger, + string $data = null, + ?Document $user = null, + string $jwt = null, string $event = null, string $eventData = null, - ?Document $user = null, - string $data = null, string $executionId = null, - string $jwt = null, - Connection $queueConnection, - ) { + ) use ( + $dbForProject, + $queueForFunctions, + $queueForUsage +) { + $user ??= new Document(); $functionId = $function->getId(); + $functionInternalId = $function->getInternalId(); $deploymentId = $function->getAttribute('deployment', ''); /** Check if deployment exists */ $deployment = $dbForProject->getDocument('deployments', $deploymentId); + $deploymentInternalId = $deployment->getInternalId(); if ($deployment->getAttribute('resourceId') !== $functionId) { throw new Exception('Deployment not found. Create deployment before trying to execute a function'); @@ -84,6 +86,8 @@ Server::setResource('execute', function () { '$id' => $executionId, '$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))], 'functionId' => $functionId, + 'functionInternalId' => $functionInternalId, + 'deploymentInternalId' => $deploymentInternalId, 'deploymentId' => $deploymentId, 'trigger' => $trigger, 'status' => 'waiting', @@ -91,7 +95,7 @@ Server::setResource('execute', function () { 'response' => '', 'stderr' => '', 'duration' => 0.0, - 'search' => implode(' ', [$functionId, $executionId]), + 'search' => implode(' ', [$function->getId(), $executionId]), ])); // TODO: @Meldiron Trigger executions.create event here @@ -99,6 +103,14 @@ Server::setResource('execute', function () { if ($execution->isEmpty()) { throw new Exception('Failed to create or read execution'); } + + /** + * Usage + */ + + $queueForUsage + ->addMetric(METRIC_EXECUTIONS, 1) // per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1); // per function } $execution->setAttribute('status', 'processing'); @@ -135,7 +147,7 @@ Server::setResource('execute', function () { variables: $vars, timeout: $function->getAttribute('timeout', 0), image: $runtime['image'], - source: $build->getAttribute('path', ''), + source: $build->getAttribute('outputPath', ''), entrypoint: $deployment->getAttribute('entrypoint', ''), ); @@ -165,7 +177,7 @@ Server::setResource('execute', function () { /** Trigger Webhook */ $executionModel = new Execution(); - $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME, $queueConnection); + $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); $executionUpdate ->setProject($project) ->setUser($user) @@ -205,31 +217,23 @@ Server::setResource('execute', function () { roles: $target['roles'] ); - /** Update usage stats */ - if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { - $usage = new Stats($statsd); - $usage - ->setParam('projectId', $project->getId()) - ->setParam('projectInternalId', $project->getInternalId()) - ->setParam('functionId', $function->getId()) // TODO: We should use functionInternalId in usage stats - ->setParam('executions.{scope}.compute', 1) - ->setParam('executionStatus', $execution->getAttribute('status', '')) - ->setParam('executionTime', $execution->getAttribute('duration')) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0) - ->submit(); - } + /** Trigger usage queue */ + $queueForUsage + ->setProject($project) + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) + ->trigger() + ; }; -}); +}, ['dbForProject', 'queueForFunctions', 'queueForUsage']); $server->job() ->inject('message') ->inject('dbForProject') ->inject('queueForFunctions') - ->inject('statsd') - ->inject('queue') + ->inject('queueForUsage') ->inject('execute') - ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Client $statsd, Connection $queue, callable $execute) { + ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage, callable $execute) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -271,20 +275,17 @@ $server->job() continue; } Console::success('Iterating function: ' . $function->getAttribute('name')); + $execute( - statsd: $statsd, - dbForProject: $dbForProject, project: $project, function: $function, - queueForFunctions: $queueForFunctions, trigger: 'event', event: $events[0], eventData: \is_string($eventData) ? $eventData : \json_encode($eventData), user: $user, data: null, executionId: null, - jwt: null, - queueConnection: $queue + jwt: null ); Console::success('Triggered function: ' . $events[0]); } @@ -312,8 +313,7 @@ $server->job() data: $data, user: $user, jwt: $jwt, - statsd: $statsd, - queueConnection: $queue + queueForUsage: $queueForUsage, ); break; case 'schedule': @@ -329,8 +329,7 @@ $server->job() data: null, user: null, jwt: null, - statsd: $statsd, - queueConnection: $queue + queueForUsage: $queueForUsage, ); break; } diff --git a/app/workers/mails.php b/app/workers/mails.php index f0658fedca..1f7edf5db4 100644 --- a/app/workers/mails.php +++ b/app/workers/mails.php @@ -1,6 +1,7 @@ getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { - return false; - } + if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { + return false; + } - return true; -} + return true; + }; +}); $server->job() ->inject('message') - ->action(function (Message $message) { + ->inject('doesLocaleExist') + ->inject('register') + ->action(function (Message $message, callable $doesLocaleExist, Registry $register) { $payload = $message->getPayload() ?? []; if (empty($payload)) { throw new Exception('Missing payload'); } - global $register; - if (empty(App::getEnv('_APP_SMTP_HOST'))) { Console::info('Skipped mail processing. No SMTP server hostname has been set.'); return; @@ -82,41 +59,50 @@ $server->job() $url = $payload['url']; $name = $payload['name']; $type = $payload['type']; - $prefix = getPrefix($type); + + $prefix = match ($type) { + MAIL_TYPE_RECOVERY => 'emails.recovery', + MAIL_TYPE_CERTIFICATE => 'emails.certificate', + MAIL_TYPE_INVITATION => 'emails.invitation', + MAIL_TYPE_VERIFICATION => 'emails.verification', + MAIL_TYPE_MAGIC_SESSION => 'emails.magicSession', + default => throw new Exception('Undefined Mail Type : ' . $type, 500) + }; + $locale = new Locale($payload['locale']); $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); - if (!doesLocaleExist($locale, $prefix)) { - $locale->setDefault('en'); - } + if (!$doesLocaleExist($locale, $prefix)) { + $locale->setDefault('en'); + } $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); $subject = ''; - switch ($type) { - case MAIL_TYPE_CERTIFICATE: - $domain = $payload['domain']; - $error = $payload['error']; - $attempt = $payload['attempt']; + switch ($type) { + case MAIL_TYPE_CERTIFICATE: + $domain = $payload['domain']; + $error = $payload['error']; + $attempt = $payload['attempt']; - $subject = \sprintf($locale->getText("$prefix.subject"), $domain); - $body->setParam('{{domain}}', $domain); - $body->setParam('{{error}}', $error); - $body->setParam('{{attempt}}', $attempt); - break; - case MAIL_TYPE_INVITATION: - $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); - $body->setParam('{{owner}}', $user->getAttribute('name')); - $body->setParam('{{team}}', $team->getAttribute('name')); - break; - case MAIL_TYPE_RECOVERY: - case MAIL_TYPE_VERIFICATION: - case MAIL_TYPE_MAGIC_SESSION: - $subject = $locale->getText("$prefix.subject"); - break; - default: - throw new Exception('Undefined Mail Type : ' . $type, 500); - } + $subject = \sprintf($locale->getText("$prefix.subject"), $domain); + $body->setParam('{{domain}}', $domain); + $body->setParam('{{error}}', $error); + $body->setParam('{{attempt}}', $attempt); + break; + case MAIL_TYPE_INVITATION: + $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); + $body->setParam('{{owner}}', $user->getAttribute('name')); + $body->setParam('{{team}}', $team->getAttribute('name')); + break; + case MAIL_TYPE_RECOVERY: + case MAIL_TYPE_VERIFICATION: + case MAIL_TYPE_MAGIC_SESSION: + $subject = $locale->getText("$prefix.subject"); + break; + default: + throw new Exception('Undefined Mail Type : ' . $type, 500); + } $body ->setParam('{{subject}}', $subject) @@ -135,7 +121,7 @@ $server->job() $body = $body->render(); - /** @var \PHPMailer\PHPMailer\PHPMailer $mail */ + /** @var PHPMailer $mail */ $mail = $register->get('smtp'); // Set project mail @@ -161,11 +147,11 @@ $server->job() $mail->Body = $body; $mail->AltBody = \strip_tags($body); - try { - $mail->send(); - } catch (\Exception $error) { - throw new Exception('Error sending mail: ' . $error->getMessage(), 500); - } + try { + $mail->send(); + } catch (\Exception $error) { + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); + } }); $server->workerStart(); diff --git a/composer.lock b/composer.lock deleted file mode 100644 index db08a93457..0000000000 --- a/composer.lock +++ /dev/null @@ -1,5205 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "70121ad826cd1782e80ebe8f91defa13", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" - }, - "time": "2020-10-02T05:23:46+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.11.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.6.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2022-11-07T16:45:52+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.3", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2021-11-25T22:38:09+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.14.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/dccc8a46586475075fbb012d8bd523b8a938c2dc", - "reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.14.0" - }, - "time": "2022-11-09T01:18:39+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.1", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-01-18T15:43:28+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303", - "shasum": "" - }, - "require": { - "composer/package-versions-deprecated": "^1.8.0", - "php": "^7.0|^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0|^8.5|^9.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A wrapper for ocramius/package-versions to get pretty versions strings", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" - }, - "time": "2021-02-04T16:20:16+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2022-04-11T09:58:17+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.8.1", - "jean85/pretty-package-versions": "^1.2", - "php": "^7.0 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "squizlabs/php_codesniffer": "^3.5, <3.5.5", - "symfony/phpunit-bridge": "5.x-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" - }, - "time": "2020-11-25T12:26:02+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.6.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.2", - "php-parallel-lint/php-console-highlighter": "^0.5.0", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.6.2", - "yoast/phpunit-polyfills": "^1.0.0" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2022-02-28T15:31:21+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.18.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.4", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.18.0" - }, - "time": "2023-02-14T09:56:04+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.20.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "shasum": "" - }, - "require": { - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.20.0" - }, - "time": "2023-02-14T09:46:54+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.30.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.0.2" - }, - "require-dev": { - "ext-mongodb": "*", - "ext-redis": "*", - "fakerphp/faker": "^1.14", - "mongodb/mongodb": "1.8.0", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistency using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.30.1" - }, - "time": "2023-02-14T06:25:03+00:00" - }, - { - "name": "utopia-php/domains", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/master" - }, - "time": "2020-02-23T07:40:02+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.26.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e", - "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.5.25", - "vimeo/psalm": "4.27.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.26.0" - }, - "time": "2023-01-13T08:14:43+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" - }, - "time": "2023-02-07T05:42:46+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.0.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.8.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.0.2" - }, - "time": "2022-11-08T11:58:46+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "0.3.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/a9e7a501f33e0da59779782359a747cb8d34cf6f", - "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.26.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.3.3" - }, - "time": "2023-03-07T08:52:22+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "0.5.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/310271c5cd477541208d7fa74a4dea64df8e04a0", - "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.2" - }, - "time": "2023-03-07T08:54:10+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.14.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" - }, - "time": "2023-03-15T00:16:34+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.6.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "289c4327713deadc9c748b5317d248133a02f245" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", - "reference": "289c4327713deadc9c748b5317d248133a02f245", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.6.0" - }, - "time": "2022-11-07T13:51:59+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.9", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70", - "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0", - "squizlabs/php_codesniffer": "3.5.4" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.9" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-01-06T12:12:50+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.29.4", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" - }, - "time": "2023-02-03T05:44:59+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" - }, - "time": "2022-05-02T15:47:09+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.70", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b", - "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.70" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2022-12-09T12:56:44+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.15.4", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" - }, - "time": "2023-03-05T19:49:14+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" - }, - "time": "2023-03-27T19:02:04+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.16.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" - }, - "time": "2023-02-07T18:11:17+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.26", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-03-06T12:58:08+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:10:38+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-02-14T08:28:10+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "4.8.9", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/4.8.9" - }, - "funding": [ - { - "url": "https://gitee.com/swoole/swoole?donate=true", - "type": "custom" - }, - { - "url": "https://github.com/swoole", - "type": "github" - } - ], - "time": "2022-04-18T20:38:04+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.5.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15", - "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.5.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-02-08T07:49:20+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.3.0" -} diff --git a/docker-compose.yml b/docker-compose.yml index d7e7259bb3..625243c1f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -614,7 +614,6 @@ services: - _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_SCHEDULES - - _APP_CONNECTIONS_QUEUE appwrite-worker-usage: entrypoint: worker-usage From 543efd6032c5f9a52dee3fcf1979d033e07b431b Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 23 May 2023 16:43:34 +0300 Subject: [PATCH 022/144] re-working worker flow --- composer.lock | 5201 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5201 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..fcddba925d --- /dev/null +++ b/composer.lock @@ -0,0 +1,5201 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "70121ad826cd1782e80ebe8f91defa13", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", + "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" + }, + "time": "2020-10-02T05:23:46+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.11.1", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.6.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2022-11-07T16:45:52+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.3", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", + "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2021-11-25T22:38:09+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-01-18T15:43:28+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "1e0104b46f045868f11942aea058cd7186d6c303" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", + "reference": "1e0104b46f045868f11942aea058cd7186d6c303", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.8.0", + "php": "^7.0|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0|^8.5|^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A wrapper for ocramius/package-versions to get pretty versions strings", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" + }, + "time": "2021-02-04T16:20:16+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", + "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2022-04-11T09:58:17+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "953dbc19443aa9314c44b7217a16873347e6840d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", + "reference": "953dbc19443aa9314c44b7217a16873347e6840d", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.8.1", + "jean85/pretty-package-versions": "^1.2", + "php": "^7.0 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.5, <3.5.5", + "symfony/phpunit-bridge": "5.x-dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" + }, + "time": "2020-11-25T12:26:02+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.6.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", + "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.2", + "php-parallel-lint/php-console-highlighter": "^0.5.0", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.6.2", + "yoast/phpunit-polyfills": "^1.0.0" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2022-02-28T15:31:21+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.18.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", + "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.30.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.4", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.18.0" + }, + "time": "2023-02-14T09:56:04+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.20.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", + "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.30.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.20.0" + }, + "time": "2023-02-14T09:46:54+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.30.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", + "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.0.2" + }, + "require-dev": { + "ext-mongodb": "*", + "ext-redis": "*", + "fakerphp/faker": "^1.14", + "mongodb/mongodb": "1.8.0", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistency using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.30.1" + }, + "time": "2023-02-14T06:25:03+00:00" + }, + { + "name": "utopia-php/domains", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/master" + }, + "time": "2020-02-23T07:40:02+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.26.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e", + "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.5.25", + "vimeo/psalm": "4.27.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.26.0" + }, + "time": "2023-01-13T08:14:43+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + }, + "time": "2023-02-07T05:42:46+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.0.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", + "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.8.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.0.2" + }, + "time": "2022-11-08T11:58:46+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "0.3.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/a9e7a501f33e0da59779782359a747cb8d34cf6f", + "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.26.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/0.3.3" + }, + "time": "2023-03-07T08:52:22+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "0.5.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/310271c5cd477541208d7fa74a4dea64df8e04a0", + "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/0.5.2" + }, + "time": "2023-03-07T08:54:10+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.14.0" + }, + "time": "2023-03-15T00:16:34+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.6.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "289c4327713deadc9c748b5317d248133a02f245" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", + "reference": "289c4327713deadc9c748b5317d248133a02f245", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.6.0" + }, + "time": "2022-11-07T13:51:59+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.9", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70", + "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0", + "squizlabs/php_codesniffer": "3.5.4" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.9" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-01-06T12:12:50+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.29.4", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", + "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" + }, + "time": "2023-02-03T05:44:59+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.70", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b", + "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.70" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2022-12-09T12:56:44+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" + }, + "time": "2023-05-19T20:20:00+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "dfc078e8af9c99210337325ff5aa152872c98714" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", + "reference": "dfc078e8af9c99210337325ff5aa152872c98714", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" + }, + "time": "2023-03-27T19:02:04+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.21.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c", + "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.0" + }, + "time": "2023-05-17T13:13:44+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-03-06T12:58:08+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "4.8.9", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", + "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/4.8.9" + }, + "funding": [ + { + "url": "https://gitee.com/swoole/swoole?donate=true", + "type": "custom" + }, + { + "url": "https://github.com/swoole", + "type": "github" + } + ], + "time": "2022-04-18T20:38:04+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "106c170d08e8415d78be2d16c3d057d0d108262b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/106c170d08e8415d78be2d16c3d057d0d108262b", + "reference": "106c170d08e8415d78be2d16c3d057d0d108262b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-05-03T19:06:57+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From f1466c05cfc9cc361949075805b4ef645cfe0284 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 29 May 2023 16:58:45 +0300 Subject: [PATCH 023/144] refactor workers --- app/config/collections.php | 11 - app/controllers/api/account.php | 262 +++++++++++++++++---- app/controllers/api/teams.php | 55 ++++- app/controllers/shared/api.php | 6 +- app/worker.php | 139 ++++++----- app/workers/audits.php | 6 +- bin/worker-audits | 9 +- bin/worker-webhooks | 9 +- composer.json | 2 +- composer.lock | 54 +++-- docker-compose.yml | 6 + src/Appwrite/Event/Event.php | 34 +-- src/Appwrite/Event/Func.php | 10 +- src/Appwrite/Platform/Appwrite.php | 2 + src/Appwrite/Platform/Services/Workers.php | 19 ++ src/Appwrite/Platform/Workers/Audits.php | 72 ++++++ src/Appwrite/Platform/Workers/Webhooks.php | 104 ++++++++ 17 files changed, 586 insertions(+), 214 deletions(-) create mode 100644 src/Appwrite/Platform/Services/Workers.php create mode 100644 src/Appwrite/Platform/Workers/Audits.php create mode 100644 src/Appwrite/Platform/Workers/Webhooks.php diff --git a/app/config/collections.php b/app/config/collections.php index 12904a1e20..5648bfe6ea 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -3410,17 +3410,6 @@ $collections = [ 'default' => null, 'filters' => [], ], - [ - 'array' => false, - '$id' => ID::custom('bucketInternalId'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'filters' => [], - ], [ '$id' => ID::custom('name'), 'type' => Database::VAR_STRING, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2680b530b9..7040496ac7 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -10,8 +10,8 @@ use Appwrite\Event\Mail; use Appwrite\Event\Phone as EventPhone; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Email; -use Appwrite\Network\Validator\Host; -use Appwrite\Network\Validator\URL; +use Utopia\Validator\Host; +use Utopia\Validator\URL; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Template\Template; use Appwrite\URL\URL as URLParser; @@ -29,10 +29,10 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\DateTime; use Utopia\Database\Exception\Duplicate; -use Utopia\Database\ID; -use Utopia\Database\Permission; +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; use Utopia\Database\Query; -use Utopia\Database\Role; +use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; @@ -44,6 +44,101 @@ use Utopia\Validator\WhiteList; $oauthDefaultSuccess = '/auth/oauth2/success'; $oauthDefaultFailure = '/auth/oauth2/failure'; +App::post('/v1/account/invite') + ->desc('Create account using an invite code') + ->groups(['api', 'account', 'auth']) + ->label('event', 'users.[userId].create') + ->label('scope', 'public') + ->label('auth.type', 'emailPassword') + ->label('audits.event', 'user.create') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'account') + ->label('sdk.method', 'createWithInviteCode') + ->label('sdk.description', '/docs/references/account/create.md') + ->label('sdk.response.code', Response::STATUS_CODE_CREATED) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('abuse-limit', 10) + ->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('email', '', new Email(), 'User email.') + ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') + ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) + ->param('code', '', new Text(128), 'An invite code to restrict user signups on the Appwrite console. Users with an invite code will be able to create accounts irrespective of email and IP whitelists.', true) + ->inject('request') + ->inject('response') + ->inject('project') + ->inject('dbForProject') + ->inject('events') + ->action(function (string $userId, string $email, string $password, string $name, string $code, Request $request, Response $response, Document $project, Database $dbForProject, Event $events) { + + if ($project->getId() !== 'console') { + throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN); + } + + $email = \strtolower($email); + + $whitelistCodes = (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_CODES', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_CODES', null)) : []; + + if (empty($whitelistCodes)) { + throw new Exception(Exception::GENERAL_CODES_DISABLED); + } + + if (!empty($whitelistCodes) && !\in_array($code, $whitelistCodes)) { + throw new Exception(Exception::USER_INVALID_CODE); + } + + $limit = $project->getAttribute('auths', [])['limit'] ?? 0; + + if ($limit !== 0) { + $total = $dbForProject->count('users', max: APP_LIMIT_USERS); + + if ($total >= $limit) { + throw new Exception(Exception::USER_COUNT_EXCEEDED); + } + } + + try { + $userId = $userId == 'unique()' ? ID::unique() : $userId; + $user = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([ + '$id' => $userId, + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ], + 'email' => $email, + 'emailVerification' => false, + 'status' => true, + 'password' => Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS), + 'hash' => Auth::DEFAULT_ALGO, + 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + 'passwordUpdate' => DateTime::now(), + 'registration' => DateTime::now(), + 'reset' => false, + 'name' => $name, + 'prefs' => new \stdClass(), + 'sessions' => null, + 'tokens' => null, + 'memberships' => null, + 'search' => implode(' ', [$userId, $email, $name]) + ]))); + } catch (Duplicate $th) { + throw new Exception(Exception::USER_ALREADY_EXISTS); + } + + Authorization::unsetRole(Role::guests()->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::users()->toString()); + + $events->setParam('userId', $user->getId()); + + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->dynamic($user, Response::MODEL_ACCOUNT); + }); + App::post('/v1/account') ->desc('Create Account') ->groups(['api', 'account', 'auth']) @@ -53,7 +148,6 @@ App::post('/v1/account') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'create') @@ -62,7 +156,7 @@ App::post('/v1/account') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) ->label('abuse-limit', 10) - ->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) @@ -78,7 +172,7 @@ App::post('/v1/account') $whitelistEmails = $project->getAttribute('authWhitelistEmails'); $whitelistIPs = $project->getAttribute('authWhitelistIPs'); - if (!empty($whitelistEmails) && !\in_array($email, $whitelistEmails)) { + if (!empty($whitelistEmails) && !\in_array($email, $whitelistEmails) && !\in_array(strtoupper($email), $whitelistEmails)) { throw new Exception(Exception::USER_EMAIL_NOT_WHITELISTED); } @@ -140,15 +234,13 @@ App::post('/v1/account') App::post('/v1/account/sessions/email') ->alias('/v1/account/sessions') ->desc('Create Email Session') - ->groups(['api', 'account', 'auth']) + ->groups(['api', 'account', 'auth', 'session']) ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') ->label('auth.type', 'emailPassword') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') - ->label('usage.metric', 'sessions.{scope}.requests.create') - ->label('usage.params', ['provider:email']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createEmailSession') @@ -188,7 +280,7 @@ App::post('/v1/account/sessions/email') $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $expire = DateTime::addSeconds(new \DateTime(), $duration); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $secret = Auth::tokenGenerator(); $session = new Document(array_merge( [ @@ -281,6 +373,12 @@ App::get('/v1/account/sessions/oauth2/:provider') $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); + $providerEnabled = $project->getAttribute('authProviders', [])[$provider . 'Enabled'] ?? false; + + if (!$providerEnabled) { + throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please enable the provider from your ' . APP_NAME . ' console to continue.'); + } + $appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? ''; $appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}'; @@ -366,7 +464,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') App::get('/v1/account/sessions/oauth2/:provider/redirect') ->desc('OAuth2 Redirect') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'session']) ->label('error', __DIR__ . '/../../views/general/error.phtml') ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') @@ -375,8 +473,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') ->label('docs', false) - ->label('usage.metric', 'sessions.{scope}.requests.create') - ->label('usage.params', ['provider:{request.provider}']) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') ->param('code', '', new Text(2048), 'OAuth2 code.') ->param('state', '', new Text(2048), 'OAuth2 state params.', true) @@ -395,6 +491,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $validateURL = new URL(); $appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? ''; $appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}'; + $providerEnabled = $project->getAttribute('authProviders', [])[$provider . 'Enabled'] ?? false; + + if (!$providerEnabled) { + throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please enable the provider from your ' . APP_NAME . ' console to continue.'); + } if (!empty($appSecret) && isset($appSecret['version'])) { $key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); @@ -622,7 +723,7 @@ App::post('/v1/account/sessions/magic-url') ->label('sdk.response.model', Response::MODEL_TOKEN) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},email:{param-email}') - ->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) ->inject('request') @@ -714,8 +815,32 @@ App::post('/v1/account/sessions/magic-url') $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $loginSecret, 'expire' => $expire, 'project' => $project->getId()]); $url = Template::unParseURL($url); + $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $project->getAttribute('name')); + + $body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl'); + $subject = $locale->getText("emails.magicSession.subject"); + + $body + ->setParam('{{subject}}', $subject) + ->setParam('{{hello}}', $locale->getText("emails.magicSession.hello")) + ->setParam('{{name}}', '') + ->setParam('{{body}}', $locale->getText("emails.magicSession.body")) + ->setParam('{{redirect}}', $url) + ->setParam('{{footer}}', $locale->getText("emails.magicSession.footer")) + ->setParam('{{thanks}}', $locale->getText("emails.magicSession.thanks")) + ->setParam('{{signature}}', $locale->getText("emails.magicSession.signature")) + ->setParam('{{project}}', $project->getAttribute('name')) + ->setParam('{{direction}}', $locale->getText('settings.direction')) + ->setParam('{{bg-body}}', '#f7f7f7') + ->setParam('{{bg-content}}', '#ffffff') + ->setParam('{{text-content}}', '#000000'); + + $body = $body->render(); + $queueForMail - ->setType(MAIL_TYPE_MAGIC_SESSION) + ->setSubject($subject) + ->setBody($body) + ->setFrom($from) ->setRecipient($user->getAttribute('email')) ->setUrl($url) ->setLocale($locale->default) @@ -740,14 +865,12 @@ App::post('/v1/account/sessions/magic-url') App::put('/v1/account/sessions/magic-url') ->desc('Create Magic URL session (confirmation)') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'session']) ->label('scope', 'public') ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('audits.event', 'session.update') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') - ->label('usage.metric', 'sessions.{scope}.requests.create') - ->label('usage.params', ['provider:magic-url']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateMagicURLSession') @@ -877,7 +1000,7 @@ App::post('/v1/account/sessions/phone') ->label('sdk.response.model', Response::MODEL_TOKEN) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},email:{param-email}') - ->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.') ->inject('request') ->inject('response') @@ -982,11 +1105,9 @@ App::post('/v1/account/sessions/phone') App::put('/v1/account/sessions/phone') ->desc('Create Phone Session (confirmation)') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'session']) ->label('scope', 'public') ->label('event', 'users.[userId].sessions.[sessionId].create') - ->label('usage.metric', 'sessions.{scope}.requests.create') - ->label('usage.params', ['provider:phone']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhoneSession') @@ -1097,15 +1218,13 @@ App::put('/v1/account/sessions/phone') App::post('/v1/account/sessions/anonymous') ->desc('Create Anonymous Session') - ->groups(['api', 'account', 'auth']) + ->groups(['api', 'account', 'auth', 'session']) ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') ->label('auth.type', 'anonymous') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') - ->label('usage.metric', 'sessions.{scope}.requests.create') - ->label('usage.params', ['provider:anonymous']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createAnonymousSession') @@ -1280,7 +1399,6 @@ App::get('/v1/account') ->desc('Get Account') ->groups(['api', 'account']) ->label('scope', 'account') - ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'get') @@ -1288,6 +1406,8 @@ App::get('/v1/account') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') ->inject('response') ->inject('user') ->action(function (Response $response, Document $user) { @@ -1298,7 +1418,6 @@ App::get('/v1/account/prefs') ->desc('Get Account Preferences') ->groups(['api', 'account']) ->label('scope', 'account') - ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'getPrefs') @@ -1306,6 +1425,8 @@ App::get('/v1/account/prefs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PREFERENCES) + ->label('sdk.offline.model', '/account/prefs') + ->label('sdk.offline.key', 'current') ->inject('response') ->inject('user') ->action(function (Response $response, Document $user) { @@ -1319,7 +1440,6 @@ App::get('/v1/account/sessions') ->desc('List Sessions') ->groups(['api', 'account']) ->label('scope', 'account') - ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'listSessions') @@ -1327,6 +1447,7 @@ App::get('/v1/account/sessions') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_SESSION_LIST) + ->label('sdk.offline.model', '/account/sessions') ->inject('response') ->inject('user') ->inject('locale') @@ -1356,7 +1477,6 @@ App::get('/v1/account/logs') ->desc('List Logs') ->groups(['api', 'account']) ->label('scope', 'account') - ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'listLogs') @@ -1417,7 +1537,6 @@ App::get('/v1/account/sessions/:sessionId') ->desc('Get Session') ->groups(['api', 'account']) ->label('scope', 'account') - ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'getSession') @@ -1425,6 +1544,8 @@ App::get('/v1/account/sessions/:sessionId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_SESSION) + ->label('sdk.offline.model', '/account/sessions') + ->label('sdk.offline.key', '{sessionId}') ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to get the current device session.') ->inject('response') ->inject('user') @@ -1463,7 +1584,6 @@ App::patch('/v1/account/name') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateName') @@ -1471,6 +1591,8 @@ App::patch('/v1/account/name') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->inject('response') ->inject('user') @@ -1495,7 +1617,6 @@ App::patch('/v1/account/password') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePassword') @@ -1534,7 +1655,6 @@ App::patch('/v1/account/email') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateEmail') @@ -1542,6 +1662,8 @@ App::patch('/v1/account/email') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('response') @@ -1586,7 +1708,6 @@ App::patch('/v1/account/phone') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhone') @@ -1594,6 +1715,8 @@ App::patch('/v1/account/phone') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('response') @@ -1634,7 +1757,6 @@ App::patch('/v1/account/prefs') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePrefs') @@ -1642,6 +1764,8 @@ App::patch('/v1/account/prefs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('sdk.offline.model', '/account/prefs') + ->label('sdk.offline.key', 'current') ->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.') ->inject('response') ->inject('user') @@ -1663,7 +1787,6 @@ App::patch('/v1/account/status') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') - ->label('usage.metric', 'users.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateStatus') @@ -1698,7 +1821,6 @@ App::delete('/v1/account/sessions/:sessionId') ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{user.$id}') - ->label('usage.metric', 'sessions.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteSession') @@ -1772,7 +1894,6 @@ App::patch('/v1/account/sessions/:sessionId') ->label('audits.event', 'session.update') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') - ->label('usage.metric', 'sessions.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateSession') @@ -1857,7 +1978,6 @@ App::delete('/v1/account/sessions') ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{user.$id}') - ->label('usage.metric', 'sessions.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteSessions') @@ -1919,7 +2039,6 @@ App::post('/v1/account/recovery') ->label('audits.event', 'recovery.create') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createRecovery') @@ -1991,12 +2110,35 @@ App::post('/v1/account/recovery') $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]); $url = Template::unParseURL($url); + $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); + $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); + $body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl'); + $subject = $locale->getText("emails.recovery.subject"); + + $body + ->setParam('{{subject}}', $subject) + ->setParam('{{hello}}', $locale->getText("emails.recovery.hello")) + ->setParam('{{name}}', $profile->getAttribute('name')) + ->setParam('{{body}}', $locale->getText("emails.recovery.body")) + ->setParam('{{redirect}}', $url) + ->setParam('{{footer}}', $locale->getText("emails.recovery.footer")) + ->setParam('{{thanks}}', $locale->getText("emails.recovery.thanks")) + ->setParam('{{signature}}', $locale->getText("emails.recovery.signature")) + ->setParam('{{project}}', $projectName) + ->setParam('{{direction}}', $locale->getText('settings.direction')) + ->setParam('{{bg-body}}', '#f7f7f7') + ->setParam('{{bg-content}}', '#ffffff') + ->setParam('{{text-content}}', '#000000'); + + $body = $body->render(); + + $queueForMail - ->setType(MAIL_TYPE_RECOVERY) ->setRecipient($profile->getAttribute('email', '')) - ->setUrl($url) - ->setLocale($locale->default) ->setName($profile->getAttribute('name')) + ->setBody($body) + ->setFrom($from) + ->setSubject($subject) ->trigger(); ; @@ -2026,7 +2168,6 @@ App::put('/v1/account/recovery') ->label('audits.event', 'recovery.update') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateRecovery') @@ -2094,7 +2235,6 @@ App::post('/v1/account/verification') ->label('event', 'users.[userId].verification.[tokenId].create') ->label('audits.event', 'verification.create') ->label('audits.resource', 'user/{response.userId}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createVerification') @@ -2151,8 +2291,31 @@ App::post('/v1/account/verification') $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]); $url = Template::unParseURL($url); + $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); + $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); + $body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl'); + $subject = $locale->getText("emails.verification.subject"); + $body + ->setParam('{{subject}}', $subject) + ->setParam('{{hello}}', $locale->getText("emails.verification.hello")) + ->setParam('{{name}}', $user->getAttribute('name')) + ->setParam('{{body}}', $locale->getText("emails.verification.body")) + ->setParam('{{redirect}}', $url) + ->setParam('{{footer}}', $locale->getText("emails.verification.footer")) + ->setParam('{{thanks}}', $locale->getText("emails.verification.thanks")) + ->setParam('{{signature}}', $locale->getText("emails.verification.signature")) + ->setParam('{{project}}', $projectName) + ->setParam('{{direction}}', $locale->getText('settings.direction')) + ->setParam('{{bg-body}}', '#f7f7f7') + ->setParam('{{bg-content}}', '#ffffff') + ->setParam('{{text-content}}', '#000000'); + + $body = $body->render(); + $queueForMail - ->setType(MAIL_TYPE_VERIFICATION) + ->setSubject($subject) + ->setBody($body) + ->setFrom($from) ->setRecipient($user->getAttribute('email')) ->setUrl($url) ->setLocale($locale->default) @@ -2184,7 +2347,6 @@ App::put('/v1/account/verification') ->label('event', 'users.[userId].verification.[tokenId].update') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{response.userId}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateVerification') @@ -2243,7 +2405,6 @@ App::post('/v1/account/verification/phone') ->label('event', 'users.[userId].verification.[tokenId].create') ->label('audits.event', 'verification.create') ->label('audits.resource', 'user/{response.userId}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createPhoneVerification') @@ -2328,7 +2489,6 @@ App::put('/v1/account/verification/phone') ->label('event', 'users.[userId].verification.[tokenId].update') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{response.userId}') - ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhoneVerification') diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index b4e59a1f6b..a5682c6112 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -7,7 +7,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Mail; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Email; -use Appwrite\Network\Validator\Host; +use Utopia\Validator\Host; use Appwrite\Template\Template; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries; @@ -25,11 +25,11 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Duplicate; -use Utopia\Database\ID; -use Utopia\Database\Permission; +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; use Utopia\Database\Query; use Utopia\Database\DateTime; -use Utopia\Database\Role; +use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; @@ -51,7 +51,7 @@ App::post('/v1/teams') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TEAM) - ->param('teamId', '', new CustomId(), 'Team ID. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('teamId', '', new CustomId(), 'Team ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', null, new Text(128), 'Team name. Max length: 128 chars.') ->param('roles', ['owner'], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', true) ->inject('response') @@ -130,6 +130,7 @@ App::get('/v1/teams') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TEAM_LIST) + ->label('sdk.offline.model', '/teams') ->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Teams::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') @@ -179,6 +180,8 @@ App::get('/v1/teams/:teamId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TEAM) + ->label('sdk.offline.model', '/teams') + ->label('sdk.offline.key', '{teamId}') ->param('teamId', '', new UID(), 'Team ID.') ->inject('response') ->inject('dbForProject') @@ -207,6 +210,8 @@ App::put('/v1/teams/:teamId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TEAM) + ->label('sdk.offline.model', '/teams') + ->label('sdk.offline.key', '{teamId}') ->param('teamId', '', new UID(), 'Team ID.') ->param('name', null, new Text(128), 'New team name. Max length: 128 chars.') ->inject('response') @@ -431,14 +436,37 @@ App::post('/v1/teams/:teamId/memberships') $url = Template::unParseURL($url); if (!$isPrivilegedUser && !$isAppUser) { // No need of confirmation when in admin or app mode + $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); + + $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); + $body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl'); + $subject = \sprintf($locale->getText("emails.invitation.subject"), $team->getAttribute('name'), $projectName); + $body->setParam('{{owner}}', $user->getAttribute('name')); + $body->setParam('{{team}}', $team->getAttribute('name')); + + $body + ->setParam('{{subject}}', $subject) + ->setParam('{{hello}}', $locale->getText("emails.invitation.hello")) + ->setParam('{{name}}', $user->getAttribute('name')) + ->setParam('{{body}}', $locale->getText("emails.invitation.body")) + ->setParam('{{redirect}}', $url) + ->setParam('{{footer}}', $locale->getText("emails.invitation.footer")) + ->setParam('{{thanks}}', $locale->getText("emails.invitation.thanks")) + ->setParam('{{signature}}', $locale->getText("emails.invitation.signature")) + ->setParam('{{project}}', $projectName) + ->setParam('{{direction}}', $locale->getText('settings.direction')) + ->setParam('{{bg-body}}', '#f7f7f7') + ->setParam('{{bg-content}}', '#ffffff') + ->setParam('{{text-content}}', '#000000'); + + $body = $body->render(); + $queueForMail - ->setType(MAIL_TYPE_INVITATION) - ->setRecipient($email) - ->setUrl($url) - ->setName($name) - ->setLocale($locale->default) - ->setTeam($team) - ->setUser($user) + ->setSubject($subject) + ->setBody($body) + ->setFrom($from) + ->setRecipient($invitee->getAttribute('email')) + ->setName($invitee->getAttribute('name')) ->trigger() ; } @@ -470,6 +498,7 @@ App::get('/v1/teams/:teamId/memberships') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST) + ->label('sdk.offline.model', '/teams/{teamId}/memberships') ->param('teamId', '', new UID(), 'Team ID.') ->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) @@ -551,6 +580,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MEMBERSHIP) + ->label('sdk.offline.model', '/teams/{teamId}/memberships') + ->label('sdk.offline.key', '{membershipId}') ->param('teamId', '', new UID(), 'Team ID.') ->param('membershipId', '', new UID(), 'Membership ID.') ->inject('response') diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 6119eebd64..305f527f8c 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -157,13 +157,12 @@ App::init() ->inject('queueForEvents') ->inject('queueForAudits') ->inject('queueForMail') - ->inject('usage') ->inject('queueForDeletes') ->inject('queueForDatabase') ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMail, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, Usage $queueForUsage, string $mode) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMail, Delete $queueForDeletes, EventDatabase $queueForDatabase, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { $route = $utopia->match($request); @@ -418,14 +417,13 @@ App::shutdown() ->inject('project') ->inject('queueForEvents') ->inject('queueForAudits') - ->inject('usage') ->inject('queueForDeletes') ->inject('queueForDatabase') ->inject('mode') ->inject('dbForProject') ->inject('queueForFunctions') ->inject('queueForUsage') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Event $queueForEvents, Audit $queueForAudits, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, string $mode, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage) use ($parseLabel) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Event $queueForEvents, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, string $mode, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage) use ($parseLabel) { $responsePayload = $response->getPayload(); diff --git a/app/worker.php b/app/worker.php index f987af0671..3cd7c8ebeb 100644 --- a/app/worker.php +++ b/app/worker.php @@ -12,6 +12,7 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Phone; use Appwrite\Event\Usage; +use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\App; use Utopia\Cache\Adapter\Sharding; @@ -20,7 +21,9 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; +use Utopia\Platform\Service; use Utopia\Queue\Adapter\Swoole; use Utopia\Queue\Message; use Utopia\Queue\Server; @@ -38,6 +41,7 @@ use Utopia\Storage\Device\S3; use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; +Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); global $register; @@ -138,72 +142,10 @@ Server::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); -Server::setResource('statsd', function ($register) { - return $register->get('statsd'); -}, ['register']); - Server::setResource('pools', function ($register) { return $register->get('pools'); }, ['register']); -$pools = $register->get('pools'); -$connection = $pools->get('queue')->pop()->getResource(); -$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); - -if (empty(App::getEnv('QUEUE'))) { - throw new Exception('Please configure "QUEUE" environemnt variable.'); -} - -$adapter = new Swoole($connection, $workerNumber, App::getEnv('QUEUE')); -$server = new Server($adapter); - -$server - ->shutdown() - ->inject('pools') - ->action(function (Group $pools) { - $pools->reclaim(); - }); - -$server - ->error() - ->inject('error') - ->inject('logger') - ->action(function (Throwable $error, Logger $logger) { - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); - - if ($error instanceof PDOException) { - throw $error; - } - - if ($error->getCode() >= 500 || $error->getCode() === 0) { - $log = new Log(); - - $log->setNamespace("appwrite-worker"); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - $log->setAction('appwrite-queue-' . App::getEnv('QUEUE')); - $log->addTag('verboseType', get_class($error)); - $log->addTag('code', $error->getCode()); - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', \Utopia\Database\Validator\Authorization::$roles); - - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - $logger->addLog($log); - } - - Console::error('[Error] Type: ' . get_class($error)); - Console::error('[Error] Message: ' . $error->getMessage()); - Console::error('[Error] File: ' . $error->getFile()); - Console::error('[Error] Line: ' . $error->getLine()); - }); - /** * Get Console DB * @@ -276,3 +218,76 @@ Server::setResource('getFilesDevice', function (string $projectId) { Server::setResource('getBuildsDevice', function (string $projectId) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); }); + +$pools = $register->get('pools'); +$platform = new Appwrite(); +$_args = (!empty($args) || !isset($_SERVER['argv']) ? $args : $_SERVER['argv']); + +if (isset($_args[0])) { + $workerName = end($_args); +} else { + throw new Exception('Missing command'); +} + +$platform->init(Service::TYPE_WORKER, [ + 'queue' => App::getEnv('QUEUE'), + 'workerNumber' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'connection' => $pools->get('queue')->pop()->getResource(), + 'name' => $workerName, +]); + + + +$worker = $platform->getWorker(); + +$worker + ->shutdown() + ->inject('pools') + ->action(function (Group $pools) { + $pools->reclaim(); + }); + +$worker + ->error() + ->inject('error') + ->inject('logger') + ->action(function (Throwable $error, Logger|null $logger) { + $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + + if ($error instanceof PDOException) { + throw $error; + } + + if ($error->getCode() >= 500 || $error->getCode() === 0) { + $log = new Log(); + + $log->setNamespace("appwrite-worker"); + $log->setServer(\gethostname()); + $log->setVersion($version); + $log->setType(Log::TYPE_ERROR); + $log->setMessage($error->getMessage()); + $log->setAction('appwrite-queue-' . App::getEnv('QUEUE')); + $log->addTag('verboseType', get_class($error)); + $log->addTag('code', $error->getCode()); + $log->addExtra('file', $error->getFile()); + $log->addExtra('line', $error->getLine()); + $log->addExtra('trace', $error->getTraceAsString()); + $log->addExtra('detailedTrace', $error->getTrace()); + $log->addExtra('roles', \Utopia\Database\Validator\Authorization::$roles); + + $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + + $logger->addLog($log); + } + + Console::error('[Error] Type: ' . get_class($error)); + Console::error('[Error] Message: ' . $error->getMessage()); + Console::error('[Error] File: ' . $error->getFile()); + Console::error('[Error] Line: ' . $error->getLine()); + }); + + + +$worker->workerStart(); +$worker->start(); diff --git a/app/workers/audits.php b/app/workers/audits.php index 5b2571e025..df386d8f06 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -28,7 +28,6 @@ Server::setResource('execute', function (Database $dbForProject) { $audit = new Audit($dbForProject); $audit->log( - userInternalId: $user->getInternalId(), userId: $user->getId(), // Pass first, most verbose event pattern event: $event, @@ -44,13 +43,13 @@ Server::setResource('execute', function (Database $dbForProject) { ] ); }; -}, ['dbForProject']); +},['dbForProject']); $server->job() ->inject('message') ->inject('dbForProject') ->inject('execute') - ->action(function (Message $message, callable $execute) { + ->action(function (Message $message, Database $dbForProject, callable $execute) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -67,6 +66,7 @@ $server->job() $user = new Document($payload['user'] ?? []); $execute( + $dbForProject, $event, $auditPayload, $mode, diff --git a/bin/worker-audits b/bin/worker-audits index 7dd25c75ca..b737ebb294 100644 --- a/bin/worker-audits +++ b/bin/worker-audits @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=1 QUEUE='v1-audits' APP_INCLUDE='/usr/src/code/app/workers/audits.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +QUEUE=v1-audits php /usr/src/code/app/worker.php audits $@ \ No newline at end of file diff --git a/bin/worker-webhooks b/bin/worker-webhooks index e34c79658a..e2ab198745 100644 --- a/bin/worker-webhooks +++ b/bin/worker-webhooks @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=0.1 QUEUE='v1-webhooks' APP_INCLUDE='/usr/src/code/app/workers/webhooks.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +QUEUE=v1-webhooks php /usr/src/code/app/worker.php webhooks $@ \ No newline at end of file diff --git a/composer.json b/composer.json index dc7cab83c8..0a3abc121a 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/database": "0.30.*", "utopia-php/queue": "0.5.*", "utopia-php/orchestration": "0.9.*", - "utopia-php/platform": "0.3.*", + "utopia-php/platform": "dev-integrate-workers as 0.3.3", "utopia-php/pools": "0.4.*", "utopia-php/preloader": "0.2.*", "utopia-php/domains": "1.1.*", diff --git a/composer.lock b/composer.lock index fcddba925d..ed235c3342 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": "70121ad826cd1782e80ebe8f91defa13", + "content-hash": "185f3be3c459767318599669f63145af", "packages": [ { "name": "adhocore/jwt", @@ -1946,16 +1946,16 @@ }, { "name": "utopia-php/platform", - "version": "0.3.3", + "version": "dev-integrate-workers", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f" + "reference": "8480c109b2dc97669f19a24d9eafeab1b5a14f2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/a9e7a501f33e0da59779782359a747cb8d34cf6f", - "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/8480c109b2dc97669f19a24d9eafeab1b5a14f2a", + "reference": "8480c109b2dc97669f19a24d9eafeab1b5a14f2a", "shasum": "" }, "require": { @@ -1963,7 +1963,8 @@ "ext-redis": "*", "php": ">=8.0", "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.26.*" + "utopia-php/framework": "0.26.*", + "utopia-php/queue": "0.5.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -1989,9 +1990,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.3.3" + "source": "https://github.com/utopia-php/platform/tree/integrate-workers" }, - "time": "2023-03-07T08:52:22+00:00" + "time": "2023-05-29T13:39:41+00:00" }, { "name": "utopia-php/pools", @@ -2099,16 +2100,16 @@ }, { "name": "utopia-php/queue", - "version": "0.5.2", + "version": "0.5.3", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0" + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/310271c5cd477541208d7fa74a4dea64df8e04a0", - "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", "shasum": "" }, "require": { @@ -2154,9 +2155,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.2" + "source": "https://github.com/utopia-php/queue/tree/0.5.3" }, - "time": "2023-03-07T08:54:10+00:00" + "time": "2023-05-24T19:06:04+00:00" }, { "name": "utopia-php/registry", @@ -3307,16 +3308,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.21.0", + "version": "1.21.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c" + "reference": "e560a3eb5e76b35d6d92377e5abb6887c1c13c95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c", - "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e560a3eb5e76b35d6d92377e5abb6887c1c13c95", + "reference": "e560a3eb5e76b35d6d92377e5abb6887c1c13c95", "shasum": "" }, "require": { @@ -3347,9 +3348,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.1" }, - "time": "2023-05-17T13:13:44+00:00" + "time": "2023-05-29T11:55:57+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5171,9 +5172,18 @@ "time": "2023-05-03T19:06:57+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/platform", + "version": "dev-integrate-workers", + "alias": "0.3.3", + "alias_normalized": "0.3.3.0" + } + ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "utopia-php/platform": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docker-compose.yml b/docker-compose.yml index 625243c1f8..c9d9844524 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -85,6 +85,8 @@ services: - ./public:/usr/src/code/public - ./src:/usr/src/code/src - ./dev:/usr/local/dev + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform + depends_on: - mariadb - redis @@ -231,6 +233,9 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform + - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue + depends_on: - redis - mariadb @@ -264,6 +269,7 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform depends_on: - redis - mariadb diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 0550063497..0fecbe0304 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -3,9 +3,8 @@ namespace Appwrite\Event; use InvalidArgumentException; +use Resque; use Utopia\Database\Document; -use Utopia\Queue\Client; -use Utopia\Queue\Connection; class Event { @@ -24,6 +23,9 @@ class Event public const FUNCTIONS_QUEUE_NAME = 'v1-functions'; public const FUNCTIONS_CLASS_NAME = 'FunctionsV1'; + public const USAGE_QUEUE_NAME = 'v1-usage'; + public const USAGE_CLASS_NAME = 'UsageV1'; + public const WEBHOOK_QUEUE_NAME = 'v1-webhooks'; public const WEBHOOK_CLASS_NAME = 'WebhooksV1'; @@ -44,18 +46,16 @@ class Event protected array $context = []; protected ?Document $project = null; protected ?Document $user = null; - protected Connection $connection; /** * @param string $queue * @param string $class * @return void */ - public function __construct(string $queue, string $class, Connection $connection) + public function __construct(string $queue, string $class) { $this->queue = $queue; $this->class = $class; - $this->connection = $connection; } /** @@ -263,11 +263,7 @@ class Event */ public function trigger(): string|bool { - $client = new Client($this->queue, $this->connection); - - $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; - - return $client->enqueue([ + return Resque::enqueue($this->queue, $this->class, [ 'project' => $this->project, 'user' => $this->user, 'payload' => $this->payload, @@ -276,18 +272,6 @@ class Event ]); } - /** - * Get Queue Size - * - * @return int - */ - public function getQueueSize(): int - { - $client = new Client($this->queue, $this->connection); - - return $client->getQueueSize(); - } - /** * Resets event. * @@ -456,9 +440,9 @@ class Event if ($subCurrent === $current || $subCurrent === $key) { continue; } - $filtered1 = \array_filter($paramKeys, fn (string $k) => $k === $subCurrent); + $filtered1 = \array_filter($paramKeys, fn(string $k) => $k === $subCurrent); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered1, '*', $eventPattern)); - $filtered2 = \array_filter($paramKeys, fn (string $k) => $k === $current); + $filtered2 = \array_filter($paramKeys, fn(string $k) => $k === $current); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered2, '*', \str_replace($filtered1, '*', $eventPattern))); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered2, '*', $eventPattern)); } @@ -466,7 +450,7 @@ class Event if ($current === $key) { continue; } - $filtered = \array_filter($paramKeys, fn (string $k) => $k === $current); + $filtered = \array_filter($paramKeys, fn(string $k) => $k === $current); $events[] = \str_replace($paramKeys, $paramValues, \str_replace($filtered, '*', $eventPattern)); } } diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index d799aa5c70..5f8b4c80c6 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -16,7 +16,7 @@ class Func extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, $connection); + parent::__construct(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME); } /** @@ -142,10 +142,6 @@ class Func extends Event */ public function trigger(): string|bool { - if ($this->paused) { - return false; - } - $client = new Client($this->queue, $this->connection); $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; @@ -166,12 +162,12 @@ class Func extends Event /** * Generate a function event from a base event * - * @param Event $queueForEvents + * @param Event $event * * @return self * */ - public function from(Event $queueForEvents): self + public function from(Event $event): self { $this->project = $event->getProject(); $this->user = $event->getUser(); diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index 9ef6f38c36..05224799f3 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform; use Appwrite\Platform\Services\Tasks; +use Appwrite\Platform\Services\Workers; use Utopia\Platform\Platform; class Appwrite extends Platform @@ -10,5 +11,6 @@ class Appwrite extends Platform public function __construct() { $this->addService('tasks', new Tasks()); + $this->addService('workers', new Workers()); } } diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php new file mode 100644 index 0000000000..1ed65a135b --- /dev/null +++ b/src/Appwrite/Platform/Services/Workers.php @@ -0,0 +1,19 @@ +type = self::TYPE_WORKER; + $this + ->addAction(Audits::getName(), new Audits()) + ->addAction(Webhooks::getName(), new webhooks()) + ; + } +} diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php new file mode 100644 index 0000000000..45929bf71e --- /dev/null +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -0,0 +1,72 @@ +desc('Audits worker') + ->inject('message') + ->inject('dbForProject') + ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); + } + + public function action(Message $message, $dbForProject): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $event = $payload['event'] ?? ''; + $auditPayload = $payload['payload'] ?? ''; + $mode = $payload['mode'] ?? ''; + $resource = $payload['resource'] ?? ''; + $userAgent = $payload['userAgent'] ?? ''; + $ip = $payload['ip'] ?? ''; + $user = new Document($payload['user'] ?? []); + + + $this->execute($event, $auditPayload, $mode, $resource, $userAgent, $ip, $user, $dbForProject); + } + + private function execute(string $event, array $payload, string $mode, string $resource, string $userAgent, string $ip, Document $user, Database $dbForProject): void + { + + $userName = $user->getAttribute('name', ''); + $userEmail = $user->getAttribute('email', ''); + + $audit = new Audit($dbForProject); + $audit->log( + userInternalId: $user->getInternalId(), + userId: $user->getId(), + // Pass first, most verbose event pattern + event: $event, + resource: $resource, + userAgent: $userAgent, + ip: $ip, + location: '', + data: [ + 'userName' => $userName, + 'userEmail' => $userEmail, + 'mode' => $mode, + 'data' => $payload, + ] + ); + } +} diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php new file mode 100644 index 0000000000..144da99aa7 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -0,0 +1,104 @@ +desc('Webhooks worker') + ->inject('message') + ->callback(fn($message) => $this->action($message)); + } + + public function action(Message $message): void + { + $payload = $message->getPayload() ?? []; + var_dump('webhooks'); + if (empty($payload)) { + throw new Exception('Missing payload'); + + + $events = $payload['events']; + $webhookPayload = json_encode($payload['payload']); + $project = new Document($payload['project']); + $user = new Document($payload['user'] ?? []); + + foreach ($project->getAttribute('webhooks', []) as $webhook) { + if (array_intersect($webhook->getAttribute('events', []), $events)) { + $this->execute($events, $webhookPayload, $webhook, $user, $project); + } + } + + if (!empty($errors)) { + throw new Exception(\implode(" / \n\n", $errors)); + } + + $this->errors = []; + } + } + + private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void + { + $url = \rawurldecode($webhook->getAttribute('url')); + $signatureKey = $webhook->getAttribute('signatureKey'); + $signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true)); + $httpUser = $webhook->getAttribute('httpUser'); + $httpPass = $webhook->getAttribute('httpPass'); + $ch = \curl_init($webhook->getAttribute('url')); + + \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + \curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + \curl_setopt($ch, CURLOPT_HEADER, 0); + \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + \curl_setopt($ch, CURLOPT_USERAGENT, \sprintf( + APP_USERAGENT, + App::getEnv('_APP_VERSION', 'UNKNOWN'), + App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) + )); + \curl_setopt( + $ch, + CURLOPT_HTTPHEADER, + [ + 'Content-Type: application/json', + 'Content-Length: ' . \strlen($payload), + 'X-' . APP_NAME . '-Webhook-Id: ' . $webhook->getId(), + 'X-' . APP_NAME . '-Webhook-Events: ' . implode(',', $events), + 'X-' . APP_NAME . '-Webhook-Name: ' . $webhook->getAttribute('name', ''), + 'X-' . APP_NAME . '-Webhook-User-Id: ' . $user->getId(), + 'X-' . APP_NAME . '-Webhook-Project-Id: ' . $project->getId(), + 'X-' . APP_NAME . '-Webhook-Signature: ' . $signature, + ] + ); + + if (!$webhook->getAttribute('security', true)) { + \curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + \curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + } + + if (!empty($httpUser) && !empty($httpPass)) { + \curl_setopt($ch, CURLOPT_USERPWD, "$httpUser:$httpPass"); + \curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + } + + if (false === \curl_exec($ch)) { + $this->errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name'); + } + + \curl_close($ch); + } +} From 5a22d174590aa1f01871da4bf87d9037486ee170 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 29 May 2023 18:03:09 +0300 Subject: [PATCH 024/144] Mails --- app/workers/mails.php | 138 +++++++++-------- bin/worker-mails | 9 +- src/Appwrite/Platform/Services/Workers.php | 6 +- src/Appwrite/Platform/Workers/Mails.php | 166 +++++++++++++++++++++ 4 files changed, 243 insertions(+), 76 deletions(-) create mode 100644 src/Appwrite/Platform/Workers/Mails.php diff --git a/app/workers/mails.php b/app/workers/mails.php index 1f7edf5db4..32fbcbcb66 100644 --- a/app/workers/mails.php +++ b/app/workers/mails.php @@ -1,45 +1,33 @@ desc('Audits worker') + ->inject('message') + ->inject('dbForProject') + ->inject('register') + ->callback(fn($message, $dbForProject, $register) => $this->action($message, $dbForProject, $register)); + } -/** - * Returns true if all the required terms in a locale exist. False otherwise - * - * @param $locale - * @param $prefix - * - * @return bool - */ -Server::setResource('doesLocaleExist', function () { - return function (Locale $locale, string $prefix) { + public function action(Message $message, $dbForProject, $register): void + { - if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { - return false; - } - - return true; - }; -}); - -$server->job() - ->inject('message') - ->inject('doesLocaleExist') - ->inject('register') - ->action(function (Message $message, callable $doesLocaleExist, Registry $register) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -72,37 +60,37 @@ $server->job() $locale = new Locale($payload['locale']); $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); - if (!$doesLocaleExist($locale, $prefix)) { - $locale->setDefault('en'); - } + if (!$doesLocaleExist($locale, $prefix)) { + $locale->setDefault('en'); + } $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); $subject = ''; - switch ($type) { - case MAIL_TYPE_CERTIFICATE: - $domain = $payload['domain']; - $error = $payload['error']; - $attempt = $payload['attempt']; + switch ($type) { + case MAIL_TYPE_CERTIFICATE: + $domain = $payload['domain']; + $error = $payload['error']; + $attempt = $payload['attempt']; - $subject = \sprintf($locale->getText("$prefix.subject"), $domain); - $body->setParam('{{domain}}', $domain); - $body->setParam('{{error}}', $error); - $body->setParam('{{attempt}}', $attempt); - break; - case MAIL_TYPE_INVITATION: - $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); - $body->setParam('{{owner}}', $user->getAttribute('name')); - $body->setParam('{{team}}', $team->getAttribute('name')); - break; - case MAIL_TYPE_RECOVERY: - case MAIL_TYPE_VERIFICATION: - case MAIL_TYPE_MAGIC_SESSION: - $subject = $locale->getText("$prefix.subject"); - break; - default: - throw new Exception('Undefined Mail Type : ' . $type, 500); - } + $subject = \sprintf($locale->getText("$prefix.subject"), $domain); + $body->setParam('{{domain}}', $domain); + $body->setParam('{{error}}', $error); + $body->setParam('{{attempt}}', $attempt); + break; + case MAIL_TYPE_INVITATION: + $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); + $body->setParam('{{owner}}', $user->getAttribute('name')); + $body->setParam('{{team}}', $team->getAttribute('name')); + break; + case MAIL_TYPE_RECOVERY: + case MAIL_TYPE_VERIFICATION: + case MAIL_TYPE_MAGIC_SESSION: + $subject = $locale->getText("$prefix.subject"); + break; + default: + throw new Exception('Undefined Mail Type : ' . $type, 500); + } $body ->setParam('{{subject}}', $subject) @@ -147,12 +135,30 @@ $server->job() $mail->Body = $body; $mail->AltBody = \strip_tags($body); - try { - $mail->send(); - } catch (\Exception $error) { - throw new Exception('Error sending mail: ' . $error->getMessage(), 500); + try { + $mail->send(); + } catch (\Exception $error) { + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); + } } - }); -$server->workerStart(); -$server->start(); + +/** + * Returns true if all the required terms in a locale exist. False otherwise + * + * @param Locale $locale + * @param string $prefix + * @return bool + */ +private function doesLocaleExist(Locale $locale, string $prefix) +{ + + if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { + return false; + } + + return true; +} + + +} \ No newline at end of file diff --git a/bin/worker-mails b/bin/worker-mails index 87fa64cf7c..0e0918ee3e 100644 --- a/bin/worker-mails +++ b/bin/worker-mails @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=1 QUEUE='v1-mails' APP_INCLUDE='/usr/src/code/app/workers/mails.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +QUEUE=v1-mails php /usr/src/code/app/worker.php mails $@ \ No newline at end of file diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 1ed65a135b..9db2b38b32 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -4,7 +4,8 @@ namespace Appwrite\Platform\Services; use Utopia\Platform\Service; use Appwrite\Platform\Workers\Audits; -use Appwrite\Platform\Workers\webhooks; +use Appwrite\Platform\Workers\Webhooks; +use Appwrite\Platform\Workers\Mails; class Workers extends Service { @@ -13,7 +14,8 @@ class Workers extends Service $this->type = self::TYPE_WORKER; $this ->addAction(Audits::getName(), new Audits()) - ->addAction(Webhooks::getName(), new webhooks()) + ->addAction(Webhooks::getName(), new Webhooks()) + ->addAction(Mails::getName(), new Mails()) ; } } diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php new file mode 100644 index 0000000000..8c9c20f519 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -0,0 +1,166 @@ +desc('Mails worker') + ->inject('message') + ->inject('register') + ->callback(fn($message, $register) => $this->action($message, $register)); + } + + public function action(Message $message, $register): void + { + + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + if (empty(App::getEnv('_APP_SMTP_HOST'))) { + Console::info('Skipped mail processing. No SMTP server hostname has been set.'); + return; + } + + $project = new Document($payload['project'] ?? []); + $user = new Document($payload['user'] ?? []); + $team = new Document($payload['team'] ?? []); + + $recipient = $payload['recipient']; + $url = $payload['url']; + $name = $payload['name']; + $type = $payload['type']; + + $prefix = match ($type) { + MAIL_TYPE_RECOVERY => 'emails.recovery', + MAIL_TYPE_CERTIFICATE => 'emails.certificate', + MAIL_TYPE_INVITATION => 'emails.invitation', + MAIL_TYPE_VERIFICATION => 'emails.verification', + MAIL_TYPE_MAGIC_SESSION => 'emails.magicSession', + default => throw new Exception('Undefined Mail Type : ' . $type, 500) + }; + + $locale = new Locale($payload['locale']); + $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); + + if (!$this->doesLocaleExist($locale, $prefix)) { + $locale->setDefault('en'); + } + + $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); + $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); + $subject = ''; + switch ($type) { + case MAIL_TYPE_CERTIFICATE: + $domain = $payload['domain']; + $error = $payload['error']; + $attempt = $payload['attempt']; + + $subject = \sprintf($locale->getText("$prefix.subject"), $domain); + $body->setParam('{{domain}}', $domain); + $body->setParam('{{error}}', $error); + $body->setParam('{{attempt}}', $attempt); + break; + case MAIL_TYPE_INVITATION: + $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); + $body->setParam('{{owner}}', $user->getAttribute('name')); + $body->setParam('{{team}}', $team->getAttribute('name')); + break; + case MAIL_TYPE_RECOVERY: + case MAIL_TYPE_VERIFICATION: + case MAIL_TYPE_MAGIC_SESSION: + $subject = $locale->getText("$prefix.subject"); + break; + default: + throw new Exception('Undefined Mail Type : ' . $type, 500); + } + + $body + ->setParam('{{subject}}', $subject) + ->setParam('{{hello}}', $locale->getText("$prefix.hello")) + ->setParam('{{name}}', $name) + ->setParam('{{body}}', $locale->getText("$prefix.body")) + ->setParam('{{redirect}}', $url) + ->setParam('{{footer}}', $locale->getText("$prefix.footer")) + ->setParam('{{thanks}}', $locale->getText("$prefix.thanks")) + ->setParam('{{signature}}', $locale->getText("$prefix.signature")) + ->setParam('{{project}}', $projectName) + ->setParam('{{direction}}', $locale->getText('settings.direction')) + ->setParam('{{bg-body}}', '#f7f7f7') + ->setParam('{{bg-content}}', '#ffffff') + ->setParam('{{text-content}}', '#000000'); + + $body = $body->render(); + + /** @var PHPMailer $mail */ + $mail = $register->get('smtp'); + + // Set project mail + /*$register->get('smtp') + ->setFrom( + App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), + ($project->getId() === 'console') + ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')) + : \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name') + ) + );*/ + + $mail->clearAddresses(); + $mail->clearAllRecipients(); + $mail->clearReplyTos(); + $mail->clearAttachments(); + $mail->clearBCCs(); + $mail->clearCCs(); + + $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), (empty($from) ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')) : $from)); + $mail->addAddress($recipient, $name); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->AltBody = \strip_tags($body); + + try { + $mail->send(); + } catch (\Exception $error) { + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); + } + } + + + /** + * Returns true if all the required terms in a locale exist. False otherwise + * + * @param Locale $locale + * @param string $prefix + * @return bool + * @throws Exception + */ + private function doesLocaleExist(Locale $locale, string $prefix): bool + { + + if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { + return false; + } + + return true; + } +} From 06ca9ce81573d039372c2195f923dcf970d52052 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 29 May 2023 19:32:33 +0300 Subject: [PATCH 025/144] Messaging --- app/workers/mails.php | 138 ++++++++++---------- bin/worker-messaging | 9 +- src/Appwrite/Platform/Services/Workers.php | 2 + src/Appwrite/Platform/Workers/Messaging.php | 101 ++++++++++++++ 4 files changed, 170 insertions(+), 80 deletions(-) create mode 100644 src/Appwrite/Platform/Workers/Messaging.php diff --git a/app/workers/mails.php b/app/workers/mails.php index 32fbcbcb66..1f7edf5db4 100644 --- a/app/workers/mails.php +++ b/app/workers/mails.php @@ -1,33 +1,45 @@ desc('Audits worker') - ->inject('message') - ->inject('dbForProject') - ->inject('register') - ->callback(fn($message, $dbForProject, $register) => $this->action($message, $dbForProject, $register)); - } +Authorization::disable(); +Authorization::setDefaultStatus(false); - public function action(Message $message, $dbForProject, $register): void - { +/** + * Returns true if all the required terms in a locale exist. False otherwise + * + * @param $locale + * @param $prefix + * + * @return bool + */ +Server::setResource('doesLocaleExist', function () { + return function (Locale $locale, string $prefix) { + if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { + return false; + } + + return true; + }; +}); + +$server->job() + ->inject('message') + ->inject('doesLocaleExist') + ->inject('register') + ->action(function (Message $message, callable $doesLocaleExist, Registry $register) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -60,37 +72,37 @@ class Mails extends Action $locale = new Locale($payload['locale']); $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); - if (!$doesLocaleExist($locale, $prefix)) { - $locale->setDefault('en'); - } + if (!$doesLocaleExist($locale, $prefix)) { + $locale->setDefault('en'); + } $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); $subject = ''; - switch ($type) { - case MAIL_TYPE_CERTIFICATE: - $domain = $payload['domain']; - $error = $payload['error']; - $attempt = $payload['attempt']; + switch ($type) { + case MAIL_TYPE_CERTIFICATE: + $domain = $payload['domain']; + $error = $payload['error']; + $attempt = $payload['attempt']; - $subject = \sprintf($locale->getText("$prefix.subject"), $domain); - $body->setParam('{{domain}}', $domain); - $body->setParam('{{error}}', $error); - $body->setParam('{{attempt}}', $attempt); - break; - case MAIL_TYPE_INVITATION: - $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); - $body->setParam('{{owner}}', $user->getAttribute('name')); - $body->setParam('{{team}}', $team->getAttribute('name')); - break; - case MAIL_TYPE_RECOVERY: - case MAIL_TYPE_VERIFICATION: - case MAIL_TYPE_MAGIC_SESSION: - $subject = $locale->getText("$prefix.subject"); - break; - default: - throw new Exception('Undefined Mail Type : ' . $type, 500); - } + $subject = \sprintf($locale->getText("$prefix.subject"), $domain); + $body->setParam('{{domain}}', $domain); + $body->setParam('{{error}}', $error); + $body->setParam('{{attempt}}', $attempt); + break; + case MAIL_TYPE_INVITATION: + $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); + $body->setParam('{{owner}}', $user->getAttribute('name')); + $body->setParam('{{team}}', $team->getAttribute('name')); + break; + case MAIL_TYPE_RECOVERY: + case MAIL_TYPE_VERIFICATION: + case MAIL_TYPE_MAGIC_SESSION: + $subject = $locale->getText("$prefix.subject"); + break; + default: + throw new Exception('Undefined Mail Type : ' . $type, 500); + } $body ->setParam('{{subject}}', $subject) @@ -135,30 +147,12 @@ class Mails extends Action $mail->Body = $body; $mail->AltBody = \strip_tags($body); - try { - $mail->send(); - } catch (\Exception $error) { - throw new Exception('Error sending mail: ' . $error->getMessage(), 500); - } + try { + $mail->send(); + } catch (\Exception $error) { + throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } + }); - -/** - * Returns true if all the required terms in a locale exist. False otherwise - * - * @param Locale $locale - * @param string $prefix - * @return bool - */ -private function doesLocaleExist(Locale $locale, string $prefix) -{ - - if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { - return false; - } - - return true; -} - - -} \ No newline at end of file +$server->workerStart(); +$server->start(); diff --git a/bin/worker-messaging b/bin/worker-messaging index 51057bc5a5..b61cf310c8 100644 --- a/bin/worker-messaging +++ b/bin/worker-messaging @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=1 QUEUE='v1-messaging' APP_INCLUDE='/usr/src/code/app/workers/messaging.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +QUEUE=v1-messaging php /usr/src/code/app/worker.php messaging $@ \ No newline at end of file diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 9db2b38b32..597bb8fae0 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -6,6 +6,7 @@ use Utopia\Platform\Service; use Appwrite\Platform\Workers\Audits; use Appwrite\Platform\Workers\Webhooks; use Appwrite\Platform\Workers\Mails; +use Appwrite\Platform\Workers\Messaging; class Workers extends Service { @@ -16,6 +17,7 @@ class Workers extends Service ->addAction(Audits::getName(), new Audits()) ->addAction(Webhooks::getName(), new Webhooks()) ->addAction(Mails::getName(), new Mails()) + ->addAction(Messaging::getName(), new Messaging()) ; } } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php new file mode 100644 index 0000000000..a58f8b171b --- /dev/null +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -0,0 +1,101 @@ +provider = App::getEnv('_APP_SMS_PROVIDER', ''); + $this->dsn = !empty($this->provider) ? new DSN($this->provider) : null; + $this->user = !empty($this->provider) ? $this->dsn->getUser() : ''; + $this->secret = !empty($this->provider) ? $this->dsn->getPassword() : ''; + + $this + ->desc('Messaging worker') + ->inject('message') + ->callback(fn($message) => $this->action($message)); + } + + public function action(Message $message): void + { + + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + if (empty($payload['recipient'])) { + throw new Exception('Missing recipient'); + } + + if (empty($payload['message'])) { + throw new Exception('Missing message'); + } + + $this->execute($payload['recipient'], $payload['message']); + } + + + private function execute(string $recipient, string $message) + { + $sms = match ($this->dsn->getHost()) { + 'mock' => new Mock($this->user, $this->secret), // used for tests + 'twilio' => new Twilio($this->user, $this->secret), + 'text-magic' => new TextMagic($this->user, $this->secret), + 'telesign' => new Telesign($this->user, $this->secret), + 'msg91' => new Msg91($this->user, $this->secret), + 'vonage' => new Vonage($this->user, $this->secret), + default => null + }; + + $from = App::getEnv('_APP_SMS_FROM'); + + if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { + Console::info('Skipped sms processing. No Phone provider has been set.'); + return; + } + + if (empty($from)) { + Console::info('Skipped sms processing. No phone number has been set.'); + return; + } + + $message = new SMS( + to: [$recipient], + content: $message, + from: $from, + ); + + try { + $sms->send($message); + } catch (\Exception $error) { + throw new Exception('Error sending message: ' . $error->getMessage(), 500); + } + } +} From 478e83a4bfe0684bce91d8ddbe3fdab86e9b407f Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 30 May 2023 11:53:52 +0300 Subject: [PATCH 026/144] Messaging --- app/worker.php | 15 ++++++--------- src/Appwrite/Platform/Workers/Audits.php | 5 +++++ src/Appwrite/Platform/Workers/Messaging.php | 16 +++++++++------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/worker.php b/app/worker.php index 3cd7c8ebeb..1108341b99 100644 --- a/app/worker.php +++ b/app/worker.php @@ -221,23 +221,20 @@ Server::setResource('getBuildsDevice', function (string $projectId) { $pools = $register->get('pools'); $platform = new Appwrite(); -$_args = (!empty($args) || !isset($_SERVER['argv']) ? $args : $_SERVER['argv']); +$args = $_SERVER['argv']; -if (isset($_args[0])) { - $workerName = end($_args); +if (isset($args[0])) { + $workerName = end($args); } else { - throw new Exception('Missing command'); + throw new Exception('Missing worker name'); } $platform->init(Service::TYPE_WORKER, [ - 'queue' => App::getEnv('QUEUE'), - 'workerNumber' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'workersNumber' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), 'connection' => $pools->get('queue')->pop()->getResource(), - 'name' => $workerName, + 'workerName' => $workerName, ]); - - $worker = $platform->getWorker(); $worker diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 45929bf71e..cbcb4a2c40 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Workers; use Exception; +use Utopia\App; use Utopia\Audit\Audit; use Utopia\Database\Database; use Utopia\Database\Document; @@ -11,6 +12,8 @@ use Utopia\Queue\Message; class Audits extends Action { + protected static array $init; + public static function getName(): string { return 'audits'; @@ -25,8 +28,10 @@ class Audits extends Action ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); } + public function action(Message $message, $dbForProject): void { + $payload = $message->getPayload() ?? []; if (empty($payload)) { diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index a58f8b171b..a75befaff3 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -18,10 +18,10 @@ use Utopia\Queue\Message; class Messaging extends Action { - private ?DSN $dsn; - private string $user; - private string $secret; - private string $provider; + private ?DSN $dsn = null; + private string $user = ''; + private string $secret = ''; + private string $provider = ''; public static function getName(): string { @@ -31,9 +31,11 @@ class Messaging extends Action public function __construct() { $this->provider = App::getEnv('_APP_SMS_PROVIDER', ''); - $this->dsn = !empty($this->provider) ? new DSN($this->provider) : null; - $this->user = !empty($this->provider) ? $this->dsn->getUser() : ''; - $this->secret = !empty($this->provider) ? $this->dsn->getPassword() : ''; + if (!empty($this->provider)) { + $this->dsn = new DSN($this->provider); + $this->user = $this->dsn->getUser(); + $this->secret = $this->dsn->getPassword(); + } $this ->desc('Messaging worker') From 73f3f0aee64113159ffd4b2d0a1e0f807131da70 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 30 May 2023 16:19:44 +0300 Subject: [PATCH 027/144] Messaging --- src/Appwrite/Platform/Workers/Audits.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index cbcb4a2c40..84a15936c5 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -12,8 +12,6 @@ use Utopia\Queue\Message; class Audits extends Action { - protected static array $init; - public static function getName(): string { return 'audits'; @@ -25,10 +23,12 @@ class Audits extends Action ->desc('Audits worker') ->inject('message') ->inject('dbForProject') - ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); + ->inject('polls') + ->callback(fn ($message, $dbForProject, $pools) => $this->action($message, $dbForProject, $pools)); } + public function action(Message $message, $dbForProject): void { From 6e7c160249c23e98ce2295a14a3e1fecafa8f187 Mon Sep 17 00:00:00 2001 From: shimon Date: Fri, 2 Jun 2023 06:54:34 +0300 Subject: [PATCH 028/144] changing event signature --- app/config/collections.php | 11 +++++++++++ app/console | 2 +- app/controllers/shared/api.php | 2 ++ app/init.php | 2 +- app/worker.php | 17 +++++++++++------ bin/worker-audits | 2 +- bin/worker-mails | 2 +- bin/worker-messaging | 2 +- bin/worker-webhooks | 2 +- docker-compose.yml | 12 ------------ src/Appwrite/Event/Audit.php | 7 ++++++- src/Appwrite/Event/Build.php | 6 +++++- src/Appwrite/Event/Certificate.php | 6 +++++- src/Appwrite/Event/Database.php | 6 +++++- src/Appwrite/Event/Delete.php | 6 +++++- src/Appwrite/Event/Event.php | 17 ++++++++--------- src/Appwrite/Event/Func.php | 6 +++++- src/Appwrite/Event/Mail.php | 6 +++++- src/Appwrite/Event/Phone.php | 6 +++++- src/Appwrite/Event/Usage.php | 6 +++++- src/Appwrite/Platform/Workers/Audits.php | 9 +++++++-- src/Appwrite/Platform/Workers/Mails.php | 9 ++++++++- src/Appwrite/Platform/Workers/Messaging.php | 7 ++++++- src/Appwrite/Platform/Workers/Webhooks.php | 5 ++++- 24 files changed, 109 insertions(+), 47 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 5648bfe6ea..12904a1e20 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -3410,6 +3410,17 @@ $collections = [ 'default' => null, 'filters' => [], ], + [ + 'array' => false, + '$id' => ID::custom('bucketInternalId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'filters' => [], + ], [ '$id' => ID::custom('name'), 'type' => Database::VAR_STRING, diff --git a/app/console b/app/console index 2fac7c1f39..9174d8f8cb 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 2fac7c1f390637f7dfc11882dcad6fb6508c83f1 +Subproject commit 9174d8f8cb584744dd7a53f69d324f490ee82ee3 diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 305f527f8c..76c985002b 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -428,9 +428,11 @@ App::shutdown() $responsePayload = $response->getPayload(); if (!empty($queueForEvents->getEvent())) { + if (empty($queueForEvents->getPayload())) { $queueForEvents->setPayload($responsePayload); } + /** * Trigger functions. */ diff --git a/app/init.php b/app/init.php index 2378d78032..d473f59373 100644 --- a/app/init.php +++ b/app/init.php @@ -882,7 +882,7 @@ App::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); App::setResource('queueForEvents', function (Connection $queue) { - return new Event('', '', $queue); + return new Event($queue); }, ['queue']); App::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); diff --git a/app/worker.php b/app/worker.php index 1108341b99..ac5a585178 100644 --- a/app/worker.php +++ b/app/worker.php @@ -12,6 +12,7 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Phone; use Appwrite\Event\Usage; +use Appwrite\Extend\Exception; use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\App; @@ -226,14 +227,18 @@ $args = $_SERVER['argv']; if (isset($args[0])) { $workerName = end($args); } else { - throw new Exception('Missing worker name'); + Console::error('Missing worker name'); } -$platform->init(Service::TYPE_WORKER, [ - 'workersNumber' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), - 'connection' => $pools->get('queue')->pop()->getResource(), - 'workerName' => $workerName, -]); +try { + $platform->init(Service::TYPE_WORKER, [ + 'workersNum' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'connection' => $pools->get('queue')->pop()->getResource(), + 'workerName' => $workerName ?? null, + ]); +} catch (\Exception $e) { + Console::error($e->getMessage() . ', File: '.$e->getFile(). ', Line: '.$e->getLine()); +} $worker = $platform->getWorker(); diff --git a/bin/worker-audits b/bin/worker-audits index b737ebb294..3df65d65e8 100644 --- a/bin/worker-audits +++ b/bin/worker-audits @@ -1,3 +1,3 @@ #!/bin/sh -QUEUE=v1-audits php /usr/src/code/app/worker.php audits $@ \ No newline at end of file +php /usr/src/code/app/worker.php audits $@ \ No newline at end of file diff --git a/bin/worker-mails b/bin/worker-mails index 0e0918ee3e..fee8a96da7 100644 --- a/bin/worker-mails +++ b/bin/worker-mails @@ -1,3 +1,3 @@ #!/bin/sh -QUEUE=v1-mails php /usr/src/code/app/worker.php mails $@ \ No newline at end of file +php /usr/src/code/app/worker.php mails $@ \ No newline at end of file diff --git a/bin/worker-messaging b/bin/worker-messaging index b61cf310c8..e6edf80f06 100644 --- a/bin/worker-messaging +++ b/bin/worker-messaging @@ -1,3 +1,3 @@ #!/bin/sh -QUEUE=v1-messaging php /usr/src/code/app/worker.php messaging $@ \ No newline at end of file +php /usr/src/code/app/worker.php messaging $@ \ No newline at end of file diff --git a/bin/worker-webhooks b/bin/worker-webhooks index e2ab198745..93f8027a81 100644 --- a/bin/worker-webhooks +++ b/bin/worker-webhooks @@ -1,3 +1,3 @@ #!/bin/sh -QUEUE=v1-webhooks php /usr/src/code/app/worker.php webhooks $@ \ No newline at end of file +php /usr/src/code/app/worker.php webhooks $@ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c9d9844524..658912aa6f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -828,18 +828,6 @@ services: # - REDIS_HOSTS=redis # ports: # - "8081:8081" - - # resque: - # image: appwrite/resque-web:1.1.0 - # networks: - # - appwrite - # ports: - # - "5678:5678" - # environment: - # - RESQUE_WEB_HOST=redis - # - RESQUE_WEB_PORT=6379 - # - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user - # - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password # webgrind: # image: 'jokkedk/webgrind:latest' # volumes: diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index 08fd3cad3f..2321fe77dd 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -14,7 +14,12 @@ class Audit extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::AUDITS_QUEUE_NAME) + ->setClass(Event::BUILDS_CLASS_NAME); + } /** diff --git a/src/Appwrite/Event/Build.php b/src/Appwrite/Event/Build.php index 1009dcedf4..6eb553d27f 100644 --- a/src/Appwrite/Event/Build.php +++ b/src/Appwrite/Event/Build.php @@ -14,7 +14,11 @@ class Build extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::BUILDS_QUEUE_NAME) + ->setClass(Event::BUILDS_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Certificate.php b/src/Appwrite/Event/Certificate.php index 1a755b7332..85058c96fe 100644 --- a/src/Appwrite/Event/Certificate.php +++ b/src/Appwrite/Event/Certificate.php @@ -13,7 +13,11 @@ class Certificate extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::CERTIFICATES_QUEUE_NAME) + ->setClass(Event::CERTIFICATES_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 7d95b347cd..0741255664 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -15,7 +15,11 @@ class Database extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::DATABASE_QUEUE_NAME) + ->setClass(Event::DATABASE_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Delete.php b/src/Appwrite/Event/Delete.php index 32c84ee90c..0bad6cb44d 100644 --- a/src/Appwrite/Event/Delete.php +++ b/src/Appwrite/Event/Delete.php @@ -17,7 +17,11 @@ class Delete extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::DELETE_QUEUE_NAME) + ->setClass(Event::DELETE_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 0fecbe0304..fec30d363b 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -3,8 +3,9 @@ namespace Appwrite\Event; use InvalidArgumentException; -use Resque; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Event { @@ -48,15 +49,10 @@ class Event protected ?Document $user = null; /** - * @param string $queue - * @param string $class + * @param Connection $connection * @return void */ - public function __construct(string $queue, string $class) - { - $this->queue = $queue; - $this->class = $class; - } + public function __construct(protected Connection $connection){} /** * Set queue used for this event. @@ -263,7 +259,10 @@ class Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + + $client = new Client($this->queue, $this->connection); + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'payload' => $this->payload, diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index 5f8b4c80c6..8cb640bf23 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -16,7 +16,11 @@ class Func extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME); + parent::__construct($connection); + + $this + ->setQueue(Event::FUNCTIONS_QUEUE_NAME) + ->setClass(Event::FUNCTIONS_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Mail.php b/src/Appwrite/Event/Mail.php index 7c1a7b53b4..6dea5f2691 100644 --- a/src/Appwrite/Event/Mail.php +++ b/src/Appwrite/Event/Mail.php @@ -17,7 +17,11 @@ class Mail extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::MAILS_QUEUE_NAME) + ->setClass(Event::MAILS_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Phone.php b/src/Appwrite/Event/Phone.php index c18b861e24..9f27345a95 100644 --- a/src/Appwrite/Event/Phone.php +++ b/src/Appwrite/Event/Phone.php @@ -12,7 +12,11 @@ class Phone extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME, $connection); + parent::__construct($connection); + + $this + ->setQueue(Event::MESSAGING_QUEUE_NAME) + ->setClass(Event::MESSAGING_CLASS_NAME); } /** diff --git a/src/Appwrite/Event/Usage.php b/src/Appwrite/Event/Usage.php index b302b88808..398c3319f2 100644 --- a/src/Appwrite/Event/Usage.php +++ b/src/Appwrite/Event/Usage.php @@ -13,7 +13,11 @@ class Usage extends Event public function __construct(protected Connection $connection) { - parent::__construct(Event::USAGE_QUEUE_NAME, Event::USAGE_CLASS_NAME); + parent::__construct($connection); + + $this + ->setQueue(Event::USAGE_QUEUE_NAME) + ->setClass(Event::USAGE_CLASS_NAME); } /** diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 84a15936c5..8c44f70d83 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -17,6 +17,9 @@ class Audits extends Action return 'audits'; } + /** + * @throws Exception + */ public function __construct() { $this @@ -28,12 +31,14 @@ class Audits extends Action } - + /** + * @throws Exception + */ public function action(Message $message, $dbForProject): void { $payload = $message->getPayload() ?? []; - + var_dump('audits worker'); if (empty($payload)) { throw new Exception('Missing payload'); } diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 8c9c20f519..b843d84908 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -19,6 +19,9 @@ class Mails extends Action return 'mails'; } + /** + * @throws Exception + */ public function __construct() { $this @@ -28,11 +31,15 @@ class Mails extends Action ->callback(fn($message, $register) => $this->action($message, $register)); } + /** + * @throws \PHPMailer\PHPMailer\Exception + * @throws Exception + */ public function action(Message $message, $register): void { $payload = $message->getPayload() ?? []; - + var_dump('mails worker'); if (empty($payload)) { throw new Exception('Missing payload'); } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index a75befaff3..ea4e130ff6 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -28,6 +28,9 @@ class Messaging extends Action return 'messaging'; } + /** + * @throws Exception + */ public function __construct() { $this->provider = App::getEnv('_APP_SMS_PROVIDER', ''); @@ -43,9 +46,11 @@ class Messaging extends Action ->callback(fn($message) => $this->action($message)); } + /** + * @throws Exception + */ public function action(Message $message): void { - $payload = $message->getPayload() ?? []; if (empty($payload)) { diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index 144da99aa7..4985c0bf07 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -25,10 +25,13 @@ class Webhooks extends Action ->callback(fn($message) => $this->action($message)); } + /** + * @throws Exception + */ public function action(Message $message): void { $payload = $message->getPayload() ?? []; - var_dump('webhooks'); + var_dump('webhooks action'); if (empty($payload)) { throw new Exception('Missing payload'); From 0d2987620c31a90cbad9c838c78394648cd92abe Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 4 Jun 2023 11:19:49 +0300 Subject: [PATCH 029/144] refactor workers --- app/worker.php | 9 +- composer.lock | 5211 ------------------- src/Appwrite/Event/Event.php | 4 +- src/Appwrite/Event/Mail.php | 84 +- src/Appwrite/Platform/Workers/Audits.php | 14 +- src/Appwrite/Platform/Workers/Mails.php | 91 +- src/Appwrite/Platform/Workers/Messaging.php | 11 +- src/Appwrite/Platform/Workers/Webhooks.php | 3 +- 8 files changed, 47 insertions(+), 5380 deletions(-) delete mode 100644 composer.lock diff --git a/app/worker.php b/app/worker.php index ac5a585178..f523defd84 100644 --- a/app/worker.php +++ b/app/worker.php @@ -237,7 +237,7 @@ try { 'workerName' => $workerName ?? null, ]); } catch (\Exception $e) { - Console::error($e->getMessage() . ', File: '.$e->getFile(). ', Line: '.$e->getLine()); + Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } $worker = $platform->getWorker(); @@ -254,6 +254,11 @@ $worker ->inject('error') ->inject('logger') ->action(function (Throwable $error, Logger|null $logger) { + + if ($logger === null) { + return; + } + $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); if ($error instanceof PDOException) { @@ -289,7 +294,5 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); - - $worker->workerStart(); $worker->start(); diff --git a/composer.lock b/composer.lock deleted file mode 100644 index ed235c3342..0000000000 --- a/composer.lock +++ /dev/null @@ -1,5211 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "185f3be3c459767318599669f63145af", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" - }, - "time": "2020-10-02T05:23:46+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.11.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.6.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2022-11-07T16:45:52+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.3", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2021-11-25T22:38:09+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.1", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-01-18T15:43:28+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303", - "shasum": "" - }, - "require": { - "composer/package-versions-deprecated": "^1.8.0", - "php": "^7.0|^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0|^8.5|^9.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A wrapper for ocramius/package-versions to get pretty versions strings", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" - }, - "time": "2021-02-04T16:20:16+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2022-04-11T09:58:17+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.8.1", - "jean85/pretty-package-versions": "^1.2", - "php": "^7.0 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "squizlabs/php_codesniffer": "^3.5, <3.5.5", - "symfony/phpunit-bridge": "5.x-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" - }, - "time": "2020-11-25T12:26:02+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.6.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.2", - "php-parallel-lint/php-console-highlighter": "^0.5.0", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.6.2", - "yoast/phpunit-polyfills": "^1.0.0" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2022-02-28T15:31:21+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.18.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.4", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.18.0" - }, - "time": "2023-02-14T09:56:04+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.20.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "shasum": "" - }, - "require": { - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.20.0" - }, - "time": "2023-02-14T09:46:54+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.30.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.0.2" - }, - "require-dev": { - "ext-mongodb": "*", - "ext-redis": "*", - "fakerphp/faker": "^1.14", - "mongodb/mongodb": "1.8.0", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistency using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.30.1" - }, - "time": "2023-02-14T06:25:03+00:00" - }, - { - "name": "utopia-php/domains", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/master" - }, - "time": "2020-02-23T07:40:02+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.26.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e", - "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.5.25", - "vimeo/psalm": "4.27.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.26.0" - }, - "time": "2023-01-13T08:14:43+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" - }, - "time": "2023-02-07T05:42:46+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.0.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.8.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.0.2" - }, - "time": "2022-11-08T11:58:46+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "dev-integrate-workers", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "8480c109b2dc97669f19a24d9eafeab1b5a14f2a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/8480c109b2dc97669f19a24d9eafeab1b5a14f2a", - "reference": "8480c109b2dc97669f19a24d9eafeab1b5a14f2a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.26.*", - "utopia-php/queue": "0.5.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/integrate-workers" - }, - "time": "2023-05-29T13:39:41+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "0.5.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.3" - }, - "time": "2023-05-24T19:06:04+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.14.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" - }, - "time": "2023-03-15T00:16:34+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.6.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "289c4327713deadc9c748b5317d248133a02f245" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", - "reference": "289c4327713deadc9c748b5317d248133a02f245", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.6.0" - }, - "time": "2022-11-07T13:51:59+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.9", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70", - "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0", - "squizlabs/php_codesniffer": "3.5.4" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.9" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-01-06T12:12:50+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.29.4", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" - }, - "time": "2023-02-03T05:44:59+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" - }, - "time": "2022-05-02T15:47:09+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.70", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b", - "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.70" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2022-12-09T12:56:44+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.15.5", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" - }, - "time": "2023-05-19T20:20:00+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" - }, - "time": "2023-03-27T19:02:04+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.21.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e560a3eb5e76b35d6d92377e5abb6887c1c13c95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e560a3eb5e76b35d6d92377e5abb6887c1c13c95", - "reference": "e560a3eb5e76b35d6d92377e5abb6887c1c13c95", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.1" - }, - "time": "2023-05-29T11:55:57+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.26", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-03-06T12:58:08+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-02-14T08:28:10+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "4.8.9", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/4.8.9" - }, - "funding": [ - { - "url": "https://gitee.com/swoole/swoole?donate=true", - "type": "custom" - }, - { - "url": "https://github.com/swoole", - "type": "github" - } - ], - "time": "2022-04-18T20:38:04+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "106c170d08e8415d78be2d16c3d057d0d108262b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/106c170d08e8415d78be2d16c3d057d0d108262b", - "reference": "106c170d08e8415d78be2d16c3d057d0d108262b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-05-03T19:06:57+00:00" - } - ], - "aliases": [ - { - "package": "utopia-php/platform", - "version": "dev-integrate-workers", - "alias": "0.3.3", - "alias_normalized": "0.3.3.0" - } - ], - "minimum-stability": "stable", - "stability-flags": { - "utopia-php/platform": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.2.0" -} diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index fec30d363b..e49ff6e8b4 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -52,7 +52,9 @@ class Event * @param Connection $connection * @return void */ - public function __construct(protected Connection $connection){} + public function __construct(protected Connection $connection) + { + } /** * Set queue used for this event. diff --git a/src/Appwrite/Event/Mail.php b/src/Appwrite/Event/Mail.php index 6dea5f2691..5fecc9540e 100644 --- a/src/Appwrite/Event/Mail.php +++ b/src/Appwrite/Event/Mail.php @@ -9,11 +9,10 @@ use Utopia\Queue\Connection; class Mail extends Event { protected string $recipient = ''; - protected string $url = ''; - protected string $type = ''; + protected string $from = ''; protected string $name = ''; - protected string $locale = ''; - protected ?Document $team = null; + protected string $subject = ''; + protected string $body = ''; public function __construct(protected Connection $connection) { @@ -25,14 +24,14 @@ class Mail extends Event } /** - * Sets team for the mail event. + * Sets subject for the mail event. * - * @param Document $team + * @param string $subject * @return self */ - public function setTeam(Document $team): self + public function setSubject(string $subject): self { - $this->team = $team; + $this->subject = $subject; return $this; } @@ -40,11 +39,11 @@ class Mail extends Event /** * Returns set team for the mail event. * - * @return null|Document + * @return string */ - public function getTeam(): ?Document + public function getSubject(): string { - return $this->team; + return $this->subject; } /** @@ -71,49 +70,49 @@ class Mail extends Event } /** - * Sets url for the mail event. + * Sets from for the mail event. * - * @param string $url + * @param string $from * @return self */ - public function setUrl(string $url): self + public function setFrom(string $from): self { - $this->url = $url; + $this->from = $from; return $this; } /** - * Returns set url for the mail event. + * Returns from for mail event. * * @return string */ - public function getURL(): string + public function getFrom(): string { - return $this->url; + return $this->from; } /** - * Sets type for the mail event (use the constants starting with MAIL_TYPE_*). + * Sets body for the mail event. * - * @param string $type + * @param string $body * @return self */ - public function setType(string $type): self + public function setBody(string $body): self { - $this->type = $type; + $this->body = $body; return $this; } /** - * Returns set type for the mail event. + * Returns body for the mail event. * * @return string */ - public function getType(): string + public function getBody(): string { - return $this->type; + return $this->body; } /** @@ -139,29 +138,6 @@ class Mail extends Event return $this->name; } - /** - * Sets locale for the mail event. - * - * @param string $locale - * @return self - */ - public function setLocale(string $locale): self - { - $this->locale = $locale; - - return $this; - } - - /** - * Returns set locale for the mail event. - * - * @return string - */ - public function getLocale(): string - { - return $this->locale; - } - /** * Executes the event and sends it to the mails worker. * @@ -172,18 +148,12 @@ class Mail extends Event { $client = new Client($this->queue, $this->connection); - $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; - return $client->enqueue([ - 'project' => $this->project, - 'user' => $this->user, - 'payload' => $this->payload, + 'from' => $this->from, 'recipient' => $this->recipient, - 'url' => $this->url, - 'locale' => $this->locale, - 'type' => $this->type, 'name' => $this->name, - 'team' => $this->team, + 'subject' => $this->subject, + 'body' => $this->body, 'events' => Event::generateEvents($this->getEvent(), $this->getParams()) ]); } diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 8c44f70d83..bd299b7b0b 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -26,8 +26,7 @@ class Audits extends Action ->desc('Audits worker') ->inject('message') ->inject('dbForProject') - ->inject('polls') - ->callback(fn ($message, $dbForProject, $pools) => $this->action($message, $dbForProject, $pools)); + ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); } @@ -38,7 +37,7 @@ class Audits extends Action { $payload = $message->getPayload() ?? []; - var_dump('audits worker'); + if (empty($payload)) { throw new Exception('Missing payload'); } @@ -51,13 +50,6 @@ class Audits extends Action $ip = $payload['ip'] ?? ''; $user = new Document($payload['user'] ?? []); - - $this->execute($event, $auditPayload, $mode, $resource, $userAgent, $ip, $user, $dbForProject); - } - - private function execute(string $event, array $payload, string $mode, string $resource, string $userAgent, string $ip, Document $user, Database $dbForProject): void - { - $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); @@ -75,7 +67,7 @@ class Audits extends Action 'userName' => $userName, 'userEmail' => $userEmail, 'mode' => $mode, - 'data' => $payload, + 'data' => $auditPayload, ] ); } diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index b843d84908..d66650f186 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -39,7 +39,7 @@ class Mails extends Action { $payload = $message->getPayload() ?? []; - var_dump('mails worker'); + if (empty($payload)) { throw new Exception('Missing payload'); } @@ -49,75 +49,11 @@ class Mails extends Action return; } - $project = new Document($payload['project'] ?? []); - $user = new Document($payload['user'] ?? []); - $team = new Document($payload['team'] ?? []); - $recipient = $payload['recipient']; - $url = $payload['url']; + $subject = $payload['subject']; $name = $payload['name']; - $type = $payload['type']; - - $prefix = match ($type) { - MAIL_TYPE_RECOVERY => 'emails.recovery', - MAIL_TYPE_CERTIFICATE => 'emails.certificate', - MAIL_TYPE_INVITATION => 'emails.invitation', - MAIL_TYPE_VERIFICATION => 'emails.verification', - MAIL_TYPE_MAGIC_SESSION => 'emails.magicSession', - default => throw new Exception('Undefined Mail Type : ' . $type, 500) - }; - - $locale = new Locale($payload['locale']); - $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]'); - - if (!$this->doesLocaleExist($locale, $prefix)) { - $locale->setDefault('en'); - } - - $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); - $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); - $subject = ''; - switch ($type) { - case MAIL_TYPE_CERTIFICATE: - $domain = $payload['domain']; - $error = $payload['error']; - $attempt = $payload['attempt']; - - $subject = \sprintf($locale->getText("$prefix.subject"), $domain); - $body->setParam('{{domain}}', $domain); - $body->setParam('{{error}}', $error); - $body->setParam('{{attempt}}', $attempt); - break; - case MAIL_TYPE_INVITATION: - $subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName); - $body->setParam('{{owner}}', $user->getAttribute('name')); - $body->setParam('{{team}}', $team->getAttribute('name')); - break; - case MAIL_TYPE_RECOVERY: - case MAIL_TYPE_VERIFICATION: - case MAIL_TYPE_MAGIC_SESSION: - $subject = $locale->getText("$prefix.subject"); - break; - default: - throw new Exception('Undefined Mail Type : ' . $type, 500); - } - - $body - ->setParam('{{subject}}', $subject) - ->setParam('{{hello}}', $locale->getText("$prefix.hello")) - ->setParam('{{name}}', $name) - ->setParam('{{body}}', $locale->getText("$prefix.body")) - ->setParam('{{redirect}}', $url) - ->setParam('{{footer}}', $locale->getText("$prefix.footer")) - ->setParam('{{thanks}}', $locale->getText("$prefix.thanks")) - ->setParam('{{signature}}', $locale->getText("$prefix.signature")) - ->setParam('{{project}}', $projectName) - ->setParam('{{direction}}', $locale->getText('settings.direction')) - ->setParam('{{bg-body}}', '#f7f7f7') - ->setParam('{{bg-content}}', '#ffffff') - ->setParam('{{text-content}}', '#000000'); - - $body = $body->render(); + $body = $payload['body']; + $from = $payload['from']; /** @var PHPMailer $mail */ $mail = $register->get('smtp'); @@ -151,23 +87,4 @@ class Mails extends Action throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } } - - - /** - * Returns true if all the required terms in a locale exist. False otherwise - * - * @param Locale $locale - * @param string $prefix - * @return bool - * @throws Exception - */ - private function doesLocaleExist(Locale $locale, string $prefix): bool - { - - if (!$locale->getText('emails.sender') || !$locale->getText("$prefix.hello") || !$locale->getText("$prefix.subject") || !$locale->getText("$prefix.body") || !$locale->getText("$prefix.footer") || !$locale->getText("$prefix.thanks") || !$locale->getText("$prefix.signature")) { - return false; - } - - return true; - } } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index ea4e130ff6..323829b130 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -65,12 +65,6 @@ class Messaging extends Action throw new Exception('Missing message'); } - $this->execute($payload['recipient'], $payload['message']); - } - - - private function execute(string $recipient, string $message) - { $sms = match ($this->dsn->getHost()) { 'mock' => new Mock($this->user, $this->secret), // used for tests 'twilio' => new Twilio($this->user, $this->secret), @@ -93,9 +87,10 @@ class Messaging extends Action return; } + $message = new SMS( - to: [$recipient], - content: $message, + to: [$payload['recipient']], + content: $payload['message'], from: $from, ); diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index 4985c0bf07..c9622dc9f9 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -31,11 +31,10 @@ class Webhooks extends Action public function action(Message $message): void { $payload = $message->getPayload() ?? []; - var_dump('webhooks action'); + if (empty($payload)) { throw new Exception('Missing payload'); - $events = $payload['events']; $webhookPayload = json_encode($payload['payload']); $project = new Document($payload['project']); From d820d93ac7c1aee9b862d47a8d00ce42b1b61f7f Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 4 Jun 2023 19:25:56 +0300 Subject: [PATCH 030/144] databases worker --- app/controllers/api/databases.php | 4 +- app/worker.php | 34 +- bin/worker-certificates | 9 +- bin/worker-databases | 9 +- src/Appwrite/Event/Event.php | 2 +- src/Appwrite/Platform/Services/Workers.php | 6 + .../Platform/Workers/Certificates.php | 440 ++++++++++++++++++ src/Appwrite/Platform/Workers/Databases.php | 321 +++++++++++++ src/Appwrite/Platform/Workers/Usage.php | 233 ++++++++++ src/Appwrite/Platform/Workers/Webhooks.php | 26 +- 10 files changed, 1026 insertions(+), 58 deletions(-) create mode 100644 src/Appwrite/Platform/Workers/Certificates.php create mode 100644 src/Appwrite/Platform/Workers/Databases.php create mode 100644 src/Appwrite/Platform/Workers/Usage.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index ffe42920b0..d29f41f6c7 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -54,7 +54,7 @@ use MaxMind\Db\Reader; * @return Document Newly created attribute document * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $database, Event $queueForEvents): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -125,7 +125,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); $dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId()); - $database + $queueForDatabase ->setType(DATABASE_TYPE_CREATE_ATTRIBUTE) ->setDatabase($db) ->setCollection($collection) diff --git a/app/worker.php b/app/worker.php index f523defd84..b76d59912d 100644 --- a/app/worker.php +++ b/app/worker.php @@ -172,27 +172,6 @@ function getCache(): Cache return new Cache(new Sharding($adapters)); } -Server::setResource('getProjectDB', function (Registry $register, Database $dbForConsole) { - return function (Document $project) use ($register, $dbForConsole) { - /** @var Group $pools */ - $pools = $register->get('pools'); - - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, getCache()); - $database->setNamespace('_' . $project->getInternalId()); - - return $database; - }; -}, ['register']); - /** * Get Functions Storage Device * @param string $projectId of the project @@ -234,7 +213,7 @@ try { $platform->init(Service::TYPE_WORKER, [ 'workersNum' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), 'connection' => $pools->get('queue')->pop()->getResource(), - 'workerName' => $workerName ?? null, + 'workerName' => strtolower($workerName) ?? null, ]); } catch (\Exception $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); @@ -255,17 +234,13 @@ $worker ->inject('logger') ->action(function (Throwable $error, Logger|null $logger) { - if ($logger === null) { - return; - } - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); if ($error instanceof PDOException) { throw $error; } - if ($error->getCode() >= 500 || $error->getCode() === 0) { + if (($error->getCode() >= 500 || $error->getCode() === 0) && !empty($logger)) { $log = new Log(); $log->setNamespace("appwrite-worker"); @@ -294,5 +269,8 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); -$worker->workerStart(); +$worker->workerStart() + ->action(function () use ($workerName) { + Console::info("Worker $workerName started"); + }); $worker->start(); diff --git a/bin/worker-certificates b/bin/worker-certificates index 679885fa46..901688c4c8 100755 --- a/bin/worker-certificates +++ b/bin/worker-certificates @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=1 QUEUE='v1-certificates' APP_INCLUDE='/usr/src/code/app/workers/certificates.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +php /usr/src/code/app/worker.php certificates $@ \ No newline at end of file diff --git a/bin/worker-databases b/bin/worker-databases index bbec58268f..502075bc58 100644 --- a/bin/worker-databases +++ b/bin/worker-databases @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=0.1 QUEUE='v1-database' APP_INCLUDE='/usr/src/code/app/workers/databases.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +php /usr/src/code/app/worker.php databases $@ \ No newline at end of file diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index e49ff6e8b4..8f3bee26e7 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -9,7 +9,7 @@ use Utopia\Queue\Connection; class Event { - public const DATABASE_QUEUE_NAME = 'v1-database'; + public const DATABASE_QUEUE_NAME = 'v1-databases'; public const DATABASE_CLASS_NAME = 'DatabaseV1'; public const DELETE_QUEUE_NAME = 'v1-deletes'; diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 597bb8fae0..8bd7522ccb 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -7,6 +7,9 @@ use Appwrite\Platform\Workers\Audits; use Appwrite\Platform\Workers\Webhooks; use Appwrite\Platform\Workers\Mails; use Appwrite\Platform\Workers\Messaging; +use Appwrite\Platform\Workers\Certificates; +use Appwrite\Platform\Workers\Databases; +use Appwrite\Platform\Workers\Usage; class Workers extends Service { @@ -18,6 +21,9 @@ class Workers extends Service ->addAction(Webhooks::getName(), new Webhooks()) ->addAction(Mails::getName(), new Mails()) ->addAction(Messaging::getName(), new Messaging()) + ->addAction(Certificates::getName(), new Certificates()) + ->addAction(Databases::getName(), new Databases()) + //->addAction(Usage::getName(), new Usage()) ; } } diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php new file mode 100644 index 0000000000..6e1d1010c1 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -0,0 +1,440 @@ +desc('Certificates worker') + ->inject('message') + ->inject('dbForConsole') + ->inject('queueForMail') + ->callback(fn($message, $dbForConsole, $queueForMail) => $this->action($message, $dbForConsole, $queueForMail)); + } + + /** + * @throws Exception|Throwable + */ + public function action(Message $message, Database $dbForConsole, Mail $queueForMail): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $document = new Document($payload['domain'] ?? []); + $domain = new Domain($document->getAttribute('domain', '')); + $skipRenewCheck = $payload['skipRenewCheck'] ?? false; + + $this->execute($domain, $dbForConsole, $queueForMail, $skipRenewCheck); + } + + /** + * @throws Exception|Throwable + */ + private function execute(Domain $domain, Database $dbForConsole, Mail $queueForMail, bool $skipRenewCheck = false): void + { + /** + * 1. Read arguments and validate domain + * 2. Get main domain + * 3. Validate CNAME DNS if parameter is not main domain (meaning it's custom domain) + * 4. Validate security email. Cannot be empty, required by LetsEncrypt + * 5. Validate renew date with certificate file, unless requested to skip by parameter + * 6. Issue a certificate using certbot CLI + * 7. Update 'log' attribute on certificate document with Certbot message + * 8. Create storage folder for certificate, if not ready already + * 9. Move certificates from Certbot location to our Storage + * 10. Create/Update our Storage with new Traefik config with new certificate paths + * 11. Read certificate file and update 'renewDate' on certificate document + * 12. Update 'issueDate' and 'attempts' on certificate + * + * If at any point unexpected error occurs, program stops without applying changes to document, and error is thrown into worker + * + * If code stops with expected error: + * 1. 'log' attribute on document is updated with error message + * 2. 'attempts' amount is increased + * 3. Console log is shown + * 4. Email is sent to security email + * + * Unless unexpected error occurs, at the end, we: + * 1. Update 'updated' attribute on document + * 2. Save document to database + * 3. Update all domains documents with current certificate ID + * + * Note: Renewals are checked and scheduled from maintenence worker + */ + + // Get current certificate + $certificate = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]); + + // If we don't have certificate for domain yet, let's create new document. At the end we save it + if (!$certificate) { + $certificate = new Document(); + $certificate->setAttribute('domain', $domain->get()); + } + + try { + // Email for alerts is required by LetsEncrypt + $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); + if (empty($email)) { + throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); + } + + // Validate domain and DNS records. Skip if job is forced + if (!$skipRenewCheck) { + $mainDomain = $this->getMainDomain($dbForConsole); + $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; + $this->validateDomain($domain, $isMainDomain); + } + + // If certificate exists already, double-check expiry date. Skip if job is forced + if (!$skipRenewCheck && !$this->isRenewRequired($domain->get())) { + throw new Exception('Renew isn\'t required.'); + } + + // Prepare folder name for certbot. Using this helps prevent miss-match in LetsEncrypt configuration when renewing certificate + $folder = ID::unique(); + + // Generate certificate files using Let's Encrypt + $letsEncryptData = $this->issueCertificate($folder, $domain->get(), $email); + + // Command succeeded, store all data into document + // We store stderr too, because it may include warnings + $certificate->setAttribute('log', \json_encode([ + 'stdout' => $letsEncryptData['stdout'], + 'stderr' => $letsEncryptData['stderr'], + ])); + + // Give certificates to Traefik + $this->applyCertificateFiles($folder, $domain->get(), $letsEncryptData); + + // Update certificate info stored in database + $certificate->setAttribute('renewDate', $this->getRenewDate($domain->get())); + $certificate->setAttribute('attempts', 0); + $certificate->setAttribute('issueDate', DateTime::now()); + } catch (Throwable $e) { + // Set exception as log in certificate document + $certificate->setAttribute('log', $e->getMessage()); + + // Increase attempts count + $attempts = $certificate->getAttribute('attempts', 0) + 1; + $certificate->setAttribute('attempts', $attempts); + + // Store cuttent time as renew date to ensure another attempt in next maintenance cycle + $certificate->setAttribute('renewDate', DateTime::now()); + + // Send email to security email + $this->notifyError($domain->get(), $e->getMessage(), $attempts, $queueForMail); + } finally { + // All actions result in new updatedAt date + $certificate->setAttribute('updated', DateTime::now()); + + // Save all changes we made to certificate document into database + $this->saveCertificateDocument($domain->get(), $certificate, $dbForConsole); + } + } + + /** + * Get main domain. Needed as we do different checks for main and non-main domains. + * + * @return null|string Returns main domain. If null, there is no main domain yet. + * @throws Exception + */ + private function getMainDomain(Database $dbForConsole): ?string + { + $envDomain = App::getEnv('_APP_DOMAIN', ''); + if (!empty($envDomain) && $envDomain !== 'localhost') { + return $envDomain; + } else { + $domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]); + if ($domainDocument) { + return $domainDocument->getAttribute('domain'); + } + } + + return null; + } + + /** + * Internal domain validation functionality to prevent unnecessary attempts failed from Let's Encrypt side. We check: + * - Domain needs to be public and valid (prevents NFT domains that are not supported by Let's Encrypt) + * - Domain must have proper DNS record + * + * @param Domain $domain Domain which we validate + * @param bool $isMainDomain In case of master domain, we look for different DNS configurations + * @return void + * @throws Exception + */ + private function validateDomain(Domain $domain, bool $isMainDomain): void + { + if (empty($domain->get())) { + throw new Exception('Missing certificate domain.'); + } + + if (!$domain->isKnown() || $domain->isTest()) { + throw new Exception('Unknown public suffix for domain.'); + } + + if (!$isMainDomain) { + // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? + // Validate if domain target is properly configured + $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + + if (!$target->isKnown() || $target->isTest()) { + throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); + } + + // Verify domain with DNS records + $validator = new CNAME($target->get()); + if (!$validator->isValid($domain->get())) { + throw new Exception('Failed to verify domain DNS records.'); + } + } else { + // Main domain validation + // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? + } + } + + /** + * Reads expiry date of certificate from file and decides if renewal is required or not. + * + * @param string $domain Domain for which we check certificate file + * @return bool True, if certificate needs to be renewed + * @throws Exception + */ + private function isRenewRequired(string $domain): bool + { + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + if (\file_exists($certPath)) { + $validTo = null; + + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? 0; + + if (empty($validTo)) { + throw new Exception('Unable to read certificate file (cert.pem).'); + } + + // LetsEncrypt allows renewal 30 days before expiry + $expiryInAdvance = (60 * 60 * 24 * 30); + if ($validTo - $expiryInAdvance > \time()) { + return false; + } + } + + return true; + } + + /** + * LetsEncrypt communication to issue certificate (using certbot CLI) + * + * @param string $folder Folder into which certificates should be generated + * @param string $domain Domain to generate certificate for + * @return array Named array with keys 'stdout' and 'stderr', both string + * @throws Exception + */ + private function issueCertificate(string $folder, string $domain, string $email): array + { + $stdout = ''; + $stderr = ''; + + $staging = (App::isProduction()) ? '' : ' --dry-run'; + $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + . " --email " . $email + . " --cert-name " . $folder + . " -w " . APP_STORAGE_CERTIFICATES + . " -d {$domain}", '', $stdout, $stderr); + + // Unexpected error, usually 5XX, API limits, ... + if ($exit !== 0) { + throw new Exception('Failed to issue a certificate with message: ' . $stderr); + } + + return [ + 'stdout' => $stdout, + 'stderr' => $stderr + ]; + } + + /** + * Method to take files from Let's Encrypt, and put it into Traefik. + * + * @param string $domain Domain which certificate was generated for + * @param string $folder Folder in which certificates were generated + * @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error + * @return void + * @throws Exception + */ + private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void + { + + // Prepare folder in storage for domain + $path = APP_STORAGE_CERTIFICATES . '/' . $domain; + if (!\is_readable($path)) { + if (!\mkdir($path, 0755, true)) { + throw new Exception('Failed to create path for certificate.'); + } + } + + // Move generated files + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + } + + $config = \implode(PHP_EOL, [ + "tls:", + " certificates:", + " - certFile: /storage/certificates/{$domain}/fullchain.pem", + " keyFile: /storage/certificates/{$domain}/privkey.pem" + ]); + + // Save configuration into Traefik using our new cert files + if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { + throw new Exception('Failed to save Traefik configuration.'); + } + } + + /** + * Method to make sure information about error is delivered to admnistrator. + * + * @param string $domain Domain that caused the error + * @param string $errorMessage Verbose error message + * @param int $attempt How many times it failed already + * @param Mail $queueForMail + * @return void + * @throws Exception + */ + private function notifyError(string $domain, string $errorMessage, int $attempt, Mail $queueForMail): void + { + // Log error into console + Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); + + // Send mail to administrator mail + $locale = new Locale(App::getEnv('_APP_LOCALE', 'en')); + if (!$locale->getText('emails.sender') || !$locale->getText("emails.certificate.hello") || !$locale->getText("emails.certificate.subject") || !$locale->getText("emails.certificate.body") || !$locale->getText("emails.certificate.footer") || !$locale->getText("emails.certificate.thanks") || !$locale->getText("emails.certificate.signature")) { + $locale->setDefault('en'); + } + + $body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl'); + + $subject = \sprintf($locale->getText("emails.certificate.subject"), $domain); + $body->setParam('{{domain}}', $domain); + $body->setParam('{{error}}', $errorMessage); + $body->setParam('{{attempt}}', $attempt); + $body + ->setParam('{{subject}}', $subject) + ->setParam('{{hello}}', $locale->getText("emails.certificate.hello")) + ->setParam('{{body}}', $locale->getText("emails.certificate.body")) + ->setParam('{{redirect}}', 'https://' . $domain) + ->setParam('{{footer}}', $locale->getText("emails.certificate.footer")) + ->setParam('{{thanks}}', $locale->getText("emails.certificate.thanks")) + ->setParam('{{signature}}', $locale->getText("emails.certificate.signature")) + ->setParam('{{project}}', 'Console') + ->setParam('{{direction}}', $locale->getText('settings.direction')) + ->setParam('{{bg-body}}', '#f7f7f7') + ->setParam('{{bg-content}}', '#ffffff') + ->setParam('{{text-content}}', '#000000'); + + $queueForMail + ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) + ->setBody($body->render()) + ->setName('Appwrite Administrator') + ->trigger(); + } + + /** + * Update all existing domain documents so they have relation to correct certificate document. + * This solved issues: + * - when adding a domain for which there is already a certificate + * - when renew creates new document? It might? + * - overall makes it more reliable + * + * @param string $certificateId ID of a new or updated certificate document + * @param string $domain Domain that is affected by new certificate + * @param Database $dbForConsole Database instance for console + * @return void + * @throws Exception + */ + private function updateDomainDocuments(string $certificateId, string $domain, Database $dbForConsole): void + { + $domains = $dbForConsole->find('domains', [ + Query::equal('domain', [$domain]), + Query::limit(1000), + ]); + + foreach ($domains as $domainDocument) { + $domainDocument->setAttribute('updated', DateTime::now()); + $domainDocument->setAttribute('certificateId', $certificateId); + $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); + + if ($domainDocument->getAttribute('projectId')) { + $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + } + } + } + + /** + * Save certificate data into database. + * + * @param string $domain Domain name that certificate is for + * @param Document $certificate Certificate document that we need to save + * @param Database $dbForConsole Database connection for console + * @return void + * @throws Exception|\Throwable + */ + private function saveCertificateDocument(string $domain, Document $certificate, Database $dbForConsole): void + { + // Check if update or insert required + $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); + if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { + // Merge new data with current data + $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + } else { + $certificate = $dbForConsole->createDocument('certificates', $certificate); + } + + $certificateId = $certificate->getId(); + $this->updateDomainDocuments($certificateId, $domain, $dbForConsole); + } +} diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php new file mode 100644 index 0000000000..dfa22988f2 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -0,0 +1,321 @@ +desc('Databases worker') + ->inject('message') + ->inject('dbForConsole') + ->inject('dbForProject') + ->callback(fn($message, $dbForConsole, $dbForProject) => $this->action($message, $dbForConsole, $dbForProject)); + } + + /** + * @throws Exception + */ + public function action(Message $message, Database $dbForConsole, Database $dbForProject): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $type = $payload['type']; + $project = new Document($payload['project']); + $collection = new Document($payload['collection'] ?? []); + $document = new Document($payload['document'] ?? []); + $database = new Document($payload['database'] ?? []); + + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + + if ($document->isEmpty()) { + throw new Exception('Missing document'); + } + + match (strval($type)) { + DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForProject), + DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForConsole, $dbForProject), + DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForProject), + DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForProject), + default => Console::error('No database operation for type: ' . $type), + }; + } + + private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, $dbForProject): void + { + + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'attributeId' => $attribute->getId() + ]); + /** + * Fetch attribute from the database, since with Resque float values are loosing informations. + */ + $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $type = $attribute->getAttribute('type', ''); + $size = $attribute->getAttribute('size', 0); + $required = $attribute->getAttribute('required', false); + $default = $attribute->getAttribute('default', null); + $signed = $attribute->getAttribute('signed', true); + $array = $attribute->getAttribute('array', false); + $format = $attribute->getAttribute('format', ''); + $formatOptions = $attribute->getAttribute('formatOptions', []); + $filters = $attribute->getAttribute('filters', []); + + try { + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create Attribute'); + } + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $attribute, + project: $project, + ); + + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + } + + /** + * @throws Authorization + */ + private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject) + { + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'attributeId' => $attribute->getId() + ]); + + $collectionId = $collection->getId(); + $key = $attribute->getAttribute('key', ''); + $status = $attribute->getAttribute('status', ''); + + // possible states at this point: + // - available: should not land in queue; controller flips these to 'deleting' + // - processing: hasn't finished creating + // - deleting: was available, in deletion queue for first time + // - failed: attribute was never created + // - stuck: attribute was available but cannot be removed + try { + if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new Exception('Failed to delete Attribute'); + } + $dbForProject->deleteDocument('attributes', $attribute->getId()); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $attribute, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + // The underlying database removes/rebuilds indexes when attribute is removed + // Update indexes table with changes + /** @var Document[] $indexes */ + $indexes = $collection->getAttribute('indexes', []); + + foreach ($indexes as $index) { + /** @var string[] $attributes */ + $attributes = $index->getAttribute('attributes'); + $lengths = $index->getAttribute('lengths'); + $orders = $index->getAttribute('orders'); + + $found = \array_search($key, $attributes); + + if ($found !== false) { + // If found, remove entry from attributes, lengths, and orders + // array_values wraps array_diff to reindex array keys + // when found attribute is removed from array + $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); + $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); + $orders = \array_values(\array_diff($orders, [$orders[$found]])); + + if (empty($attributes)) { + $dbForProject->deleteDocument('indexes', $index->getId()); + } else { + $index + ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) + ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) + ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); + + // Check if an index exists with the same attributes and orders + $exists = false; + foreach ($indexes as $existing) { + if ( + $existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself + && $existing->getAttribute('attributes') === $index->getAttribute('attributes') + && $existing->getAttribute('orders') === $index->getAttribute('orders') + ) { + $exists = true; + break; + } + } + + if ($exists) { // Delete the duplicate if created, else update in db + $this->deleteIndex($database, $collection, $index, $project, $dbForConsole); + } else { + $dbForProject->updateDocument('indexes', $index->getId(), $index); + } + } + } + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + } + + private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForProject) + { + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'indexId' => $index->getId() + ]); + $collectionId = $collection->getId(); + $key = $index->getAttribute('key', ''); + $type = $index->getAttribute('type', ''); + $attributes = $index->getAttribute('attributes', []); + $lengths = $index->getAttribute('lengths', []); + $orders = $index->getAttribute('orders', []); + + try { + if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + throw new Exception('Failed to create Index'); + } + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $index, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $index->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); + } + + private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForProject) + { + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId(), + 'indexId' => $index->getId() + ]); + $key = $index->getAttribute('key'); + $status = $index->getAttribute('status', ''); + + try { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new Exception('Failed to delete index'); + } + $dbForProject->deleteDocument('indexes', $index->getId()); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); + } finally { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $index, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $index->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $project->getId(), + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } + + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); + } +} diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php new file mode 100644 index 0000000000..7546cdf9bc --- /dev/null +++ b/src/Appwrite/Platform/Workers/Usage.php @@ -0,0 +1,233 @@ + 'Y-m-d H:00', + '1d' => 'Y-m-d 00:00', + 'inf' => '0000-00-00 00:00' + ]; + + + const INFINITY_PERIOD = '_inf_'; + + + public static function getName(): string + { + return 'usage'; + } + + /** + * @throws Exception + */ + public function __construct() + { + $this + ->desc('Usage worker') + ->inject('message') + ->inject('pools') + ->inject('cache') + ->callback(function ($message, $pools, $cache) use (&$stats) { + $this->action($message, $pools, $cache); + }); + } + + /** + * @throws Exception + */ + public function action(Message $message, $pools, $cache): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $payload = $message->getPayload() ?? []; + $project = new Document($payload['project'] ?? []); + $projectId = $project->getInternalId(); + foreach ($payload['reduce'] ?? [] as $document) { + if (empty($document)) { + continue; + } + + $this->reduce( + database: $project->getAttribute('database'), + projectInternalId: $project->getInternalId(), + document: new Document($document), + metrics: $payload['metrics'], + pools: $pools, + cache: $cache + ); + } + + $stats[$projectId]['database'] = $project->getAttribute('database'); + foreach ($payload['metrics'] ?? [] as $metric) { + if (!isset($stats[$projectId]['keys'][$metric['key']])) { + $stats[$projectId]['keys'][$metric['key']] = $metric['value']; + continue; + } + $stats[$projectId]['keys'][$metric['key']] += $metric['value']; + } + } + + +/** +* On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files. +* When we remove a parent document we need to deduct his children aggregation from the project scope. +*/ + private function reduce($database, $projectInternalId, Document $document, array &$metrics, $pools, $cache) + { + try { + $dbForProject = new Database( + $pools + ->get($database) + ->pop() + ->getResource(), + $cache + ); + + $dbForProject->setNamespace('_' . $projectInternalId); + + switch (true) { + case $document->getCollection() === 'users': // users + $sessions = count($document->getAttribute(METRIC_SESSIONS, 0)); + if (!empty($sessions)) { + $metrics[] = [ + 'key' => METRIC_SESSIONS, + 'value' => ($sessions * -1), + ]; + } + break; + case $document->getCollection() === 'databases': // databases + $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); + $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS))); + if (!empty($collections['value'])) { + $metrics[] = [ + 'key' => METRIC_COLLECTIONS, + 'value' => ($collections['value'] * -1), + ]; + } + + if (!empty($documents['value'])) { + $metrics[] = [ + 'key' => METRIC_DOCUMENTS, + 'value' => ($documents['value'] * -1), + ]; + } + break; + case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections + $parts = explode('_', $document->getCollection()); + $databaseInternalId = $parts[1] ?? 0; + $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS))); + + if (!empty($documents['value'])) { + $metrics[] = [ + 'key' => METRIC_DOCUMENTS, + 'value' => ($documents['value'] * -1), + ]; + $metrics[] = [ + 'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), + 'value' => ($documents['value'] * -1), + ]; + } + break; + + case $document->getCollection() === 'buckets': + $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); + $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); + + if (!empty($files['value'])) { + $metrics[] = [ + 'key' => METRIC_FILES, + 'value' => ($files['value'] * -1), + ]; + } + + if (!empty($storage['value'])) { + $metrics[] = [ + 'key' => METRIC_FILES_STORAGE, + 'value' => ($storage['value'] * -1), + ]; + } + break; + + case $document->getCollection() === 'functions': + $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS))); + $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE))); + $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS))); + $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE))); + $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE))); + $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS))); + $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE))); + + if (!empty($deployments['value'])) { + $metrics[] = [ + 'key' => METRIC_DEPLOYMENTS, + 'value' => ($deployments['value'] * -1), + ]; + } + + if (!empty($deploymentsStorage['value'])) { + $metrics[] = [ + 'key' => METRIC_DEPLOYMENTS_STORAGE, + 'value' => ($deploymentsStorage['value'] * -1), + ]; + } + + if (!empty($builds['value'])) { + $metrics[] = [ + 'key' => METRIC_BUILDS, + 'value' => ($builds['value'] * -1), + ]; + } + + if (!empty($buildsStorage['value'])) { + $metrics[] = [ + 'key' => METRIC_BUILDS_STORAGE, + 'value' => ($buildsStorage['value'] * -1), + ]; + } + + if (!empty($buildsCompute['value'])) { + $metrics[] = [ + 'key' => METRIC_BUILDS_COMPUTE, + 'value' => ($buildsCompute['value'] * -1), + ]; + } + + if (!empty($executions['value'])) { + $metrics[] = [ + 'key' => METRIC_EXECUTIONS, + 'value' => ($executions['value'] * -1), + ]; + } + + if (!empty($executionsCompute['value'])) { + $metrics[] = [ + 'key' => METRIC_EXECUTIONS_COMPUTE, + 'value' => ($executionsCompute['value'] * -1), + ]; + } + break; + default: + break; + } + } catch (\Exception $e) { + console::error("[reducer] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}"); + } catch (\Throwable $e) { + } finally { + $pools->reclaim(); + } + } +} diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index c9622dc9f9..fb40ffb8fe 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -17,6 +17,9 @@ class Webhooks extends Action return 'webhooks'; } + /** + * @throws Exception + */ public function __construct() { $this @@ -34,26 +37,27 @@ class Webhooks extends Action if (empty($payload)) { throw new Exception('Missing payload'); + } - $events = $payload['events']; - $webhookPayload = json_encode($payload['payload']); - $project = new Document($payload['project']); - $user = new Document($payload['user'] ?? []); + $events = $payload['events']; + $webhookPayload = json_encode($payload['payload']); + $project = new Document($payload['project']); + $user = new Document($payload['user'] ?? []); - foreach ($project->getAttribute('webhooks', []) as $webhook) { - if (array_intersect($webhook->getAttribute('events', []), $events)) { + foreach ($project->getAttribute('webhooks', []) as $webhook) { + if (array_intersect($webhook->getAttribute('events', []), $events)) { $this->execute($events, $webhookPayload, $webhook, $user, $project); - } } + } - if (!empty($errors)) { - throw new Exception(\implode(" / \n\n", $errors)); - } + if (!empty($errors)) { + throw new Exception(\implode(" / \n\n", $errors)); + } $this->errors = []; - } } + private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void { $url = \rawurldecode($webhook->getAttribute('url')); From 62c619434e25f94ab8e23e00c3ab754a45abe2ec Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 5 Jun 2023 19:13:00 +0300 Subject: [PATCH 031/144] functions/builds/deletes worker --- app/cli.php | 9 +- app/init.php | 4 +- app/worker.php | 57 +- bin/worker-builds | 9 +- bin/worker-deletes | 9 +- bin/worker-functions | 2 +- docker-compose.yml | 4 + src/Appwrite/Platform/Services/Workers.php | 6 + src/Appwrite/Platform/Tasks/Maintenance.php | 205 +++-- src/Appwrite/Platform/Workers/Builds.php | 290 +++++++ src/Appwrite/Platform/Workers/Deletes.php | 851 ++++++++++++++++++++ src/Appwrite/Platform/Workers/Functions.php | 350 ++++++++ 12 files changed, 1646 insertions(+), 150 deletions(-) create mode 100644 src/Appwrite/Platform/Workers/Builds.php create mode 100644 src/Appwrite/Platform/Workers/Deletes.php create mode 100644 src/Appwrite/Platform/Workers/Functions.php diff --git a/app/cli.php b/app/cli.php index 5da82fa7de..7beab28715 100644 --- a/app/cli.php +++ b/app/cli.php @@ -26,10 +26,13 @@ use Utopia\Database\Document; use Utopia\Logger\Log; use Utopia\Pools\Group; use Utopia\Queue\Connection; +use Utopia\Queue\Server; use Utopia\Registry\Registry; Authorization::disable(); +global $register; + CLI::setResource('register', fn () => $register); CLI::setResource('cache', function ($pools) { @@ -95,7 +98,7 @@ CLI::setResource('dbForConsole', function ($pools, $cache) { CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -121,8 +124,6 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $database; }; - - return $getProjectDB; }, ['pools', 'dbForConsole', 'cache']); CLI::setResource('queue', function (Group $pools) { @@ -144,7 +145,7 @@ CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); CLI::setResource('queueForEvents', function (Connection $queue) { - return new Event('', '', $queue); + return new Event($queue); }, ['queue']); CLI::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); diff --git a/app/init.php b/app/init.php index d473f59373..7ded34755b 100644 --- a/app/init.php +++ b/app/init.php @@ -614,13 +614,13 @@ $register->set('pools', function () { foreach ($connections as $key => $connection) { $type = $connection['type'] ?? ''; $dsns = $connection['dsns'] ?? ''; - $multipe = $connection['multiple'] ?? false; + $multiple = $connection['multiple'] ?? false; $schemes = $connection['schemes'] ?? []; $config = []; $dsns = explode(',', $connection['dsns'] ?? ''); foreach ($dsns as &$dsn) { $dsn = explode('=', $dsn); - $name = ($multipe) ? $key . '_' . $dsn[0] : $key; + $name = ($multiple) ? $key . '_' . $dsn[0] : $key; $dsn = $dsn[1] ?? ''; $config[] = $name; if (empty($dsn)) { diff --git a/app/worker.php b/app/worker.php index b76d59912d..08c38ebd8b 100644 --- a/app/worker.php +++ b/app/worker.php @@ -12,7 +12,6 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Phone; use Appwrite\Event\Usage; -use Appwrite\Extend\Exception; use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\App; @@ -23,9 +22,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\DSN\DSN; use Utopia\Platform\Service; -use Utopia\Queue\Adapter\Swoole; use Utopia\Queue\Message; use Utopia\Queue\Server; use Utopia\Registry\Registry; @@ -34,13 +31,6 @@ use Utopia\Logger\Logger; use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Storage\Device; -use Utopia\Storage\Device\Backblaze; -use Utopia\Storage\Device\DOSpaces; -use Utopia\Storage\Device\Linode; -use Utopia\Storage\Device\Local; -use Utopia\Storage\Device\S3; -use Utopia\Storage\Device\Wasabi; -use Utopia\Storage\Storage; Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); @@ -81,6 +71,37 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, return $adapter; }, ['cache', 'register', 'message', 'dbForConsole']); +Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + $databaseName = $project->getAttribute('database'); + + if (isset($databases[$databaseName])) { + $database = $databases[$databaseName]; + $database->setNamespace('_' . $project->getInternalId()); + return $database; + } + + $dbAdapter = $pools + ->get($databaseName) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + + $databases[$databaseName] = $database; + + $database->setNamespace('_' . $project->getInternalId()); + + return $database; + }; +}, ['pools', 'dbForConsole', 'cache']); + Server::setResource('cache', function (Registry $register) { $pools = $register->get('pools'); $list = Config::getParam('pools-cache', []); @@ -95,20 +116,12 @@ Server::setResource('cache', function (Registry $register) { return new Cache(new Sharding($adapters)); }, ['register']); - -Server::setResource('queueForDatabase', function (Registry $register) { - $pools = $register->get('pools'); - return new EventDatabase( - $pools - ->get('queue') - ->pop() - ->getResource() - ); -}, ['register']); - Server::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); +Server::setResource('queueForDatabase', function (Connection $queue) { + return new EventDatabase($queue); +}, ['queue']); Server::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); @@ -125,7 +138,7 @@ Server::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); Server::setResource('queueForEvents', function (Connection $queue) { - return new Event('', '', $queue); + return new Event($queue); }, ['queue']); Server::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); diff --git a/bin/worker-builds b/bin/worker-builds index 2ba26ef4d1..3400111cb5 100644 --- a/bin/worker-builds +++ b/bin/worker-builds @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=0.1 QUEUE='v1-builds' APP_INCLUDE='/usr/src/code/app/workers/builds.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +php /usr/src/code/app/worker.php builds $@ \ No newline at end of file diff --git a/bin/worker-deletes b/bin/worker-deletes index 02c2311fa7..7c9793e6cb 100644 --- a/bin/worker-deletes +++ b/bin/worker-deletes @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=1 QUEUE='v1-deletes' APP_INCLUDE='/usr/src/code/app/workers/deletes.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +php /usr/src/code/app/worker.php deletes $@ \ No newline at end of file diff --git a/bin/worker-functions b/bin/worker-functions index 687f9fd0cd..4757b1b72a 100644 --- a/bin/worker-functions +++ b/bin/worker-functions @@ -1,3 +1,3 @@ #!/bin/sh -QUEUE=v1-functions php /usr/src/code/app/workers/functions.php $@ \ No newline at end of file +php /usr/src/code/app/worker.php functions $@ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 658912aa6f..59baa08ba9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -235,6 +235,7 @@ services: - ./src:/usr/src/code/src - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue + - ./vendor/utopia-php/pools:/usr/src/code/vendor/utopia-php/pools depends_on: - redis @@ -592,6 +593,8 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/pools:/usr/src/code/vendor/utopia-php/pools + depends_on: - redis environment: @@ -614,6 +617,7 @@ services: - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT - _APP_CONNECTIONS_CACHE + - _APP_CONNECTIONS_QUEUE - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 8bd7522ccb..6ad47e32bb 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -9,6 +9,9 @@ use Appwrite\Platform\Workers\Mails; use Appwrite\Platform\Workers\Messaging; use Appwrite\Platform\Workers\Certificates; use Appwrite\Platform\Workers\Databases; +use Appwrite\Platform\Workers\Functions; +use Appwrite\Platform\Workers\Builds; +use Appwrite\Platform\Workers\Deletes; use Appwrite\Platform\Workers\Usage; class Workers extends Service @@ -23,6 +26,9 @@ class Workers extends Service ->addAction(Messaging::getName(), new Messaging()) ->addAction(Certificates::getName(), new Certificates()) ->addAction(Databases::getName(), new Databases()) + ->addAction(Functions::getName(), new Functions()) + ->addAction(Builds::getName(), new Builds()) + ->addAction(Deletes::getName(), new Deletes()) //->addAction(Usage::getName(), new Usage()) ; } diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 4327ca30eb..214505ff74 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -2,10 +2,8 @@ namespace Appwrite\Platform\Tasks; -use Appwrite\Auth\Auth; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; -use Appwrite\Event\Func; use Utopia\App; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -13,8 +11,6 @@ use Utopia\Database\Document; use Utopia\Database\DateTime; use Utopia\Database\Query; use Utopia\Platform\Action; -use Utopia\Queue\Connection; -use Utopia\Registry\Registry; class Maintenance extends Action { @@ -38,104 +34,12 @@ class Maintenance extends Action Console::title('Maintenance V1'); Console::success(APP_NAME . ' maintenance process v1 has started'); - function notifyDeleteExecutionLogs(int $interval, Delete $queueForDeletes) - { - ($queueForDeletes) - ->setType(DELETE_TYPE_EXECUTIONS) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) - ->trigger(); - } - - function notifyDeleteAbuseLogs(int $interval, Delete $queueForDeletes) - { - ($queueForDeletes) - ->setType(DELETE_TYPE_ABUSE) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) - ->trigger(); - } - - function notifyDeleteAuditLogs(int $interval, Delete $queueForDeletes) - { - ($queueForDeletes) - ->setType(DELETE_TYPE_AUDIT) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) - ->trigger(); - } - - function notifyDeleteUsageStats(int $usageStatsRetentionHourly, Delete $queueForDeletes) - { - ($queueForDeletes) - ->setType(DELETE_TYPE_USAGE) - ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) - ->trigger(); - } - - function notifyDeleteConnections(Delete $queueForDeletes) - { - ($queueForDeletes) - ->setType(DELETE_TYPE_REALTIME) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -60)) - ->trigger(); - } - - function notifyDeleteExpiredSessions(Delete $queueForDeletes) - { - ($queueForDeletes) - ->setType(DELETE_TYPE_SESSIONS) - ->trigger(); - } - - function renewCertificates(Database $dbForConsole, Certificate $queueForCertificate) - { - $time = DateTime::now(); - - $certificates = $dbForConsole->find('certificates', [ - Query::lessThan('attempts', 5), // Maximum 5 attempts - Query::lessThanEqual('renewDate', $time), // includes 60 days cooldown (we have 30 days to renew) - Query::limit(200), // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains) - ]); - - - if (\count($certificates) > 0) { - Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); - - foreach ($certificates as $certificate) { - $queueForCertificate - ->setDomain(new Document([ - 'domain' => $certificate->getAttribute('domain') - ])) - ->trigger(); - } - } else { - Console::info("[{$time}] No certificates for renewal."); - } - } - - function notifyDeleteCache($interval, Delete $queueForDeletes) - { - - ($queueForDeletes) - ->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) - ->trigger(); - } - - function notifyDeleteSchedules($interval, Delete $queueForDeletes) - { - - ($queueForDeletes) - ->setType(DELETE_TYPE_SCHEDULES) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) - ->trigger(); - } - // # of days in seconds (1 day = 86400s) $interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400'); $executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600'); $auditLogRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', '1209600'); $abuseLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', '86400'); $usageStatsRetentionHourly = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_HOURLY', '8640000'); //100 days - $cacheRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days $schedulesDeletionRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day @@ -143,15 +47,106 @@ class Maintenance extends Action $time = DateTime::now(); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); - notifyDeleteExecutionLogs($executionLogsRetention, $queueForDeletes); - notifyDeleteAbuseLogs($abuseLogsRetention, $queueForDeletes); - notifyDeleteAuditLogs($auditLogRetention, $queueForDeletes); - notifyDeleteUsageStats($usageStatsRetentionHourly, $queueForDeletes); - notifyDeleteConnections($queueForDeletes); - notifyDeleteExpiredSessions($queueForDeletes); - renewCertificates($dbForConsole, $queueForCertificates); - notifyDeleteCache($cacheRetention, $queueForDeletes); - notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes); + $this->notifyDeleteExecutionLogs($executionLogsRetention, $queueForDeletes); + $this->notifyDeleteAbuseLogs($abuseLogsRetention, $queueForDeletes); + $this->notifyDeleteAuditLogs($auditLogRetention, $queueForDeletes); + $this->notifyDeleteUsageStats($usageStatsRetentionHourly, $queueForDeletes); + $this->notifyDeleteConnections($queueForDeletes); + $this->notifyDeleteExpiredSessions($queueForDeletes); + $this->renewCertificates($dbForConsole, $queueForCertificates); + $this->notifyDeleteCache($cacheRetention, $queueForDeletes); + $this->notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes); }, $interval); } + + private function notifyDeleteExecutionLogs(int $interval, Delete $queueForDeletes): void + { + ($queueForDeletes) + ->setType(DELETE_TYPE_EXECUTIONS) + ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->trigger(); + } + + private function notifyDeleteAbuseLogs(int $interval, Delete $queueForDeletes): void + { + ($queueForDeletes) + ->setType(DELETE_TYPE_ABUSE) + ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->trigger(); + } + + private function notifyDeleteAuditLogs(int $interval, Delete $queueForDeletes): void + { + ($queueForDeletes) + ->setType(DELETE_TYPE_AUDIT) + ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->trigger(); + } + + private function notifyDeleteUsageStats(int $usageStatsRetentionHourly, Delete $queueForDeletes): void + { + ($queueForDeletes) + ->setType(DELETE_TYPE_USAGE) + ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) + ->trigger(); + } + + private function notifyDeleteConnections(Delete $queueForDeletes): void + { + ($queueForDeletes) + ->setType(DELETE_TYPE_REALTIME) + ->setDatetime(DateTime::addSeconds(new \DateTime(), -60)) + ->trigger(); + } + + private function notifyDeleteExpiredSessions(Delete $queueForDeletes): void + { + ($queueForDeletes) + ->setType(DELETE_TYPE_SESSIONS) + ->trigger(); + } + + private function renewCertificates(Database $dbForConsole, Certificate $queueForCertificate): void + { + $time = DateTime::now(); + + $certificates = $dbForConsole->find('certificates', [ + Query::lessThan('attempts', 5), // Maximum 5 attempts + Query::lessThanEqual('renewDate', $time), // includes 60 days cooldown (we have 30 days to renew) + Query::limit(200), // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains) + ]); + + + if (\count($certificates) > 0) { + Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); + + foreach ($certificates as $certificate) { + $queueForCertificate + ->setDomain(new Document([ + 'domain' => $certificate->getAttribute('domain') + ])) + ->trigger(); + } + } else { + Console::info("[{$time}] No certificates for renewal."); + } + } + + private function notifyDeleteCache($interval, Delete $queueForDeletes): void + { + + ($queueForDeletes) + ->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP) + ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->trigger(); + } + + private function notifyDeleteSchedules($interval, Delete $queueForDeletes): void + { + + ($queueForDeletes) + ->setType(DELETE_TYPE_SCHEDULES) + ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->trigger(); + } } diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php new file mode 100644 index 0000000000..93c0bc7589 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -0,0 +1,290 @@ +desc('Builds worker') + ->inject('message') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForFunctions') + ->inject('queueForUsage') + ->callback(fn($message, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage) => $this->action($message, $dbForProject, $queueForEvents, $queueForFunctions, $queueForUsage)); + } + + /** + * @throws Exception|\Throwable + */ + public function action(Message $message, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $type = $payload['type'] ?? ''; + $project = new Document($payload['project'] ?? []); + $resource = new Document($payload['resource'] ?? []); + $deployment = new Document($payload['deployment'] ?? []); + + switch ($type) { + case BUILD_TYPE_DEPLOYMENT: + case BUILD_TYPE_RETRY: + Console::info('Creating build for deployment: ' . $deployment->getId()); + $this->buildDeployment( + dbForProject: $dbForProject, + queueForEvents: $queueForEvents, + queueForFunctions: $queueForFunctions, + queueForUsage: $queueForUsage, + deployment: $deployment, + project: $project, + function: $resource + ); + break; + + default: + throw new \Exception('Invalid build type'); + } + } + + /** + * @throws Authorization + * @throws \Throwable + * @throws Structure + */ + private function buildDeployment(Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Document $deployment, Document $project, Document $function): void + { + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $function = $dbForProject->getDocument('functions', $function->getId()); + + if ($function->isEmpty()) { + throw new Exception('Function not found', 404); + } + + $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); + if ($deployment->isEmpty()) { + throw new Exception('Deployment not found', 404); + } + + $runtimes = Config::getParam('runtimes', []); + $key = $function->getAttribute('runtime'); + $runtime = $runtimes[$key] ?? null; + if (\is_null($runtime)) { + throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); + } + + $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); + /** @TODO : move this to the registry or someplace else */ + $device = Storage::DEVICE_LOCAL; + try { + $dsn = new DSN($connection); + $device = $dsn->getScheme(); + } catch (\Exception $e) { + Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } + + $buildId = $deployment->getAttribute('buildId', ''); + $startTime = DateTime::now(); + + if (empty($buildId)) { + $buildId = ID::unique(); + $build = $dbForProject->createDocument('builds', new Document([ + '$id' => $buildId, + '$permissions' => [], + 'startTime' => $startTime, + 'deploymentId' => $deployment->getId(), + 'status' => 'processing', + 'path' => '', + 'size' => 0, + 'runtime' => $function->getAttribute('runtime'), + 'source' => $deployment->getAttribute('path'), + 'sourceType' => $device, + 'stdout' => '', + 'stderr' => '', + 'duration' => 0 + ])); + + $deployment->setAttribute('buildId', $buildId); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + } else { + $build = $dbForProject->getDocument('builds', $buildId); + } + + /** Request the executor to build the code... */ + $build->setAttribute('status', 'building'); + $build = $dbForProject->updateDocument('builds', $buildId, $build); + + /** Trigger Webhook */ + $deploymentUpdate = $queueForEvents + ->setQueue(Event::WEBHOOK_QUEUE_NAME) + ->setClass(Event::WEBHOOK_CLASS_NAME) + ->setProject($project) + ->setEvent('functions.[functionId].deployments.[deploymentId].update') + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($deployment->getArrayCopy( + array_keys( + (new Deployment())->getRules() + ) + )); + + $deploymentUpdate->trigger(); + + /** Trigger Functions */ + $queueForFunctions + ->from($deploymentUpdate) + ->trigger(); + + + /** Trigger Realtime */ + $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ + 'functionId' => $function->getId(), + 'deploymentId' => $deployment->getId() + ]); + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + + $source = $deployment->getAttribute('path'); + + $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { + $carry[$var->getAttribute('key')] = $var->getAttribute('value'); + return $carry; + }, []); + + try { + $response = $executor->createRuntime( + deploymentId: $deployment->getId(), + projectId: $project->getId(), + source: $source, + image: $runtime['image'], + remove: true, + entrypoint: $deployment->getAttribute('entrypoint'), + workdir: '/usr/code', + destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", + variables: $vars, + commands: [ + 'sh', '-c', + 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ + cd /usr/local/src/ && ./build.sh' + ] + ); + + /** Update the build document */ + $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); + $build->setAttribute('duration', \intval($response['duration'])); + $build->setAttribute('status', $response['status']); + $build->setAttribute('path', $response['path']); + $build->setAttribute('size', $response['size']); + $build->setAttribute('stderr', $response['stderr']); + $build->setAttribute('stdout', $response['stdout']); + + /* Also update the deployment buildTime */ + $deployment->setAttribute('buildTime', $response['duration']); + + Console::success("Build id: $buildId created"); + + $function->setAttribute('scheduleUpdatedAt', DateTime::now()); + + /** Set auto deploy */ + if ($deployment->getAttribute('activate') === true) { + $function->setAttribute('deployment', $deployment->getId()); + $function = $dbForProject->updateDocument('functions', $function->getId(), $function); + } + + /** Update function schedule */ + $dbForConsole = getConsoleDB(); + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); + + $schedule + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + + + \Utopia\Database\Validator\Authorization::skip(fn() => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + } catch (\Throwable $th) { + $endTime = DateTime::now(); + $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); + $build->setAttribute('duration', $interval->format('%s') + 0); + $build->setAttribute('status', 'failed'); + $build->setAttribute('stderr', $th->getMessage()); + Console::error($th->getMessage()); + } finally { + $build = $dbForProject->updateDocument('builds', $buildId, $build); + + /** + * Send realtime Event + */ + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project + ); + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + + /** Trigger usage queue */ + $queueForUsage + ->setProject($project) + ->addMetric(METRIC_BUILDS, 1) // per project + ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) + ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) + ->trigger(); + } + } +} diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php new file mode 100644 index 0000000000..bf53c396c3 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -0,0 +1,851 @@ +desc('Deletes worker') + ->inject('message') + ->inject('dbForConsole') + ->inject('getProjectDB') + ->inject('getFilesDevice') + ->inject('getFunctionsDevice') + ->inject('getBuildsDevice') + ->inject('getCacheDevice') + ->callback(fn($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice) => $this->action($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice)); + } + + /** + * @throws Exception + * @throws \Throwable + */ + public function action(Message $message, Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $type = $payload['type'] ?? ''; + $datetime = $payload['datetime'] ?? null; + $hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null; + $resource = $payload['resource'] ?? null; + $document = new Document($payload['document'] ?? []); + $project = new Document($payload['project'] ?? []); + + switch (strval($type)) { + case DELETE_TYPE_DOCUMENT: + switch ($document->getCollection()) { + case DELETE_TYPE_DATABASES: + $this->deleteDatabase($getProjectDB, $document, $project); + break; + case DELETE_TYPE_COLLECTIONS: + $this->deleteCollection($getProjectDB, $document, $project); + break; + case DELETE_TYPE_PROJECTS: + $this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document); + break; + case DELETE_TYPE_FUNCTIONS: + $this->deleteFunction($getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); + break; + case DELETE_TYPE_DEPLOYMENTS: + $this->deleteDeployment($getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); + break; + case DELETE_TYPE_USERS: + $this->deleteUser($getProjectDB, $document, $project); + break; + case DELETE_TYPE_TEAMS: + $this->deleteMemberships($getProjectDB, $document, $project); + break; + case DELETE_TYPE_BUCKETS: + $this->deleteBucket($getProjectDB, $getFilesDevice, $document, $project); + break; + default: + if (\str_starts_with($document->getCollection(), 'database_')) { + $this->deleteCollection($getProjectDB, $document, $project); + break; + } + Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); + break; + } + break; + + case DELETE_TYPE_EXECUTIONS: + $this->deleteExecutionLogs($dbForConsole, $getProjectDB, $datetime); + break; + + case DELETE_TYPE_AUDIT: + if (!empty($datetime)) { + $this->deleteAuditLogs($dbForConsole, $getProjectDB, $datetime); + } + + if (!$document->isEmpty()) { + $this->deleteAuditLogsByResource($getProjectDB, 'document/' . $document->getId(), $project); + } + + break; + + case DELETE_TYPE_ABUSE: + $this->deleteAbuseLogs($dbForConsole, $getProjectDB, $datetime); + break; + + case DELETE_TYPE_REALTIME: + $this->deleteRealtimeUsage($dbForConsole, $getProjectDB, $datetime); + break; + + case DELETE_TYPE_SESSIONS: + $this->deleteExpiredSessions($dbForConsole, $getProjectDB); + break; + + case DELETE_TYPE_CERTIFICATES: + $this->deleteCertificates($dbForConsole, $document); + break; + + case DELETE_TYPE_USAGE: + $this->deleteUsageStats($dbForConsole, $getProjectDB, $hourlyUsageRetentionDatetime); + break; + + case DELETE_TYPE_CACHE_BY_RESOURCE: + $this->deleteCacheByResource($getProjectDB, $resource); + break; + case DELETE_TYPE_CACHE_BY_TIMESTAMP: + $this->deleteCacheByDate($getProjectDB); + break; + case DELETE_TYPE_SCHEDULES: + $this->deleteSchedules($dbForConsole, $getProjectDB, $datetime); + break; + default: + Console::error('No delete operation for type: ' . $type); + break; + } + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param string $datetime + * @return void + * @throws Authorization + * @throws \Throwable + */ + protected function deleteSchedules(Database $dbForConsole, callable $getProjectDB, string $datetime): void + { + $this->listByGroup( + 'schedules', + [ + Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), + Query::equal('resourceType', ['function']), + Query::lessThanEqual('resourceUpdatedAt', $datetime), + Query::equal('active', [false]), + ], + $dbForConsole, + function (Document $document) use ($dbForConsole, $getProjectDB) { + $project = $dbForConsole->getDocument('projects', $document->getAttribute('projectId')); + + if ($project->isEmpty()) { + Console::warning('Unable to delete schedule for function ' . $document->getAttribute('resourceId')); + return; + } + + $function = $getProjectDB($project)->getDocument('functions', $document->getAttribute('resourceId')); + + if ($function->isEmpty()) { + $dbForConsole->deleteDocument('schedules', $document->getId()); + Console::success('Deleting schedule for function ' . $document->getAttribute('resourceId')); + } + } + ); + } + + /** + * @param callable $getProjectDB + * @param string $resource + * @throws Exception + */ + protected function deleteCacheByResource(callable $getProjectDB, string $resource): void + { + $this->deleteCacheFiles($getProjectDB, [ + Query::equal('resource', [$resource]), + ]); + } + + /** + * @throws Exception + */ + protected function deleteCacheByDate(callable $getProjectDB): void + { + $this->deleteCacheFiles($getProjectDB, [ + Query::lessThan('accessedAt', $this->args['datetime']), + ]); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param array $query + * @return void + * @throws Exception + */ + protected function deleteCacheFiles(Database $dbForConsole, callable $getProjectDB, array $query): void + { + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($query, $getProjectDB) { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $cache = new Cache( + new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) + ); + + $this->deleteByGroup( + 'cache', + $query, + $dbForProject, + function (Document $document) use ($cache, $projectId) { + $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); + + if ($cache->purge($document->getId())) { + Console::success('Deleting cache file: ' . $path); + } else { + Console::error('Failed to delete cache file: ' . $path); + } + } + ); + }); + } + + /** + * @param callable $getProjectDB + * @param Document $document database document + * @param Document $project + * @throws Exception + */ + protected function deleteDatabase(callable $getProjectDB, Document $document, Document $project): void + { + $databaseId = $document->getId(); + $dbForProject = $getProjectDB($project); + + $this->deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($getProjectDB, $project) { + $this->deleteCollection($getProjectDB, $document, $project); + }); + + $dbForProject->deleteCollection('database_' . $document->getInternalId()); + $this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId, $project); + } + + /** + * @param callable $getProjectDB + * @param Document $document teams document + * @param Document $project + * @throws Exception + */ + protected function deleteCollection(callable $getProjectDB, Document $document, Document $project): void + { + $collectionId = $document->getId(); + $databaseId = $document->getAttribute('databaseId'); + $databaseInternalId = $document->getAttribute('databaseInternalId'); + $dbForProject = $getProjectDB($project); + + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); + $this->deleteByGroup('attributes', [ + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$collectionId]) + ], $dbForProject); + + $this->deleteByGroup('indexes', [ + Query::equal('databaseId', [$databaseId]), + Query::equal('collectionId', [$collectionId]) + ], $dbForProject); + + $this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId . '/collection/' . $collectionId, $project); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param string $hourlyUsageRetentionDatetime + * @throws Exception + */ + protected function deleteUsageStats(Database $dbForConsole, callable $getProjectDB, string $hourlyUsageRetentionDatetime) + { + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $hourlyUsageRetentionDatetime) { + $dbForProject = $getProjectDB($project); + // Delete Usage stats + $this->deleteByGroup('stats', [ + Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::equal('period', ['1h']), + ], $dbForProject); + }); + } + + /** + * @param callable $getProjectDB + * @param Document $document teams document + * @param Document $project + * @throws Exception + */ + protected function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void + { + $teamId = $document->getAttribute('teamId', ''); + + // Delete Memberships + $this->deleteByGroup('memberships', [ + Query::equal('teamId', [$teamId]) + ], $getProjectDB($project)); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param callable $getFilesDevice + * @param callable $getFunctionsDevice + * @param callable $getBuildsDevice + * @param callable $getCacheDevice + * @param Document $document project document + * @throws Authorization + */ + protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void + { + $projectId = $document->getId(); + + // Delete project domains and certificates + $domains = $dbForConsole->find('domains', [ + Query::equal('projectInternalId', [$document->getInternalId()]) + ]); + + foreach ($domains as $domain) { + $this->deleteCertificates($dbForConsole, $domain); + } + + // Delete project tables + $dbForProject = $getProjectDB($projectId, $document); + + while (true) { + $collections = $dbForProject->listCollections(); + + if (empty($collections)) { + break; + } + + foreach ($collections as $collection) { + $dbForProject->deleteCollection($collection->getId()); + } + } + + // Delete metadata tables + try { + $dbForProject->deleteCollection('_metadata'); + } catch (Exception) { + // Ignore: deleteCollection tries to delete a metadata entry after the collection is deleted, + // which will throw an exception here because the metadata collection is already deleted. + } + + // Delete all storage directories + $uploads = $getFilesDevice($projectId); + $functions = $getFunctionsDevice($projectId); + $builds = $getBuildsDevice($projectId); + $cache = $getCacheDevice($projectId); + + $uploads->delete($uploads->getRoot(), true); + $functions->delete($functions->getRoot(), true); + $builds->delete($builds->getRoot(), true); + $cache->delete($cache->getRoot(), true); + } + + /** + * @param callable $getProjectDB + * @param Document $document user document + * @param Document $project + * @throws Exception + */ + protected function deleteUser(callable $getProjectDB, Document $document, Document $project): void + { + $userId = $document->getId(); + $dbForProject = $getProjectDB($project); + + // Delete all sessions of this user from the sessions table and update the sessions field of the user record + $this->deleteByGroup('sessions', [ + Query::equal('userId', [$userId]) + ], $dbForProject); + + $dbForProject->deleteCachedDocument('users', $userId); + + // Delete Memberships and decrement team membership counts + $this->deleteByGroup('memberships', [ + Query::equal('userId', [$userId]) + ], $dbForProject, function (Document $document) use ($dbForProject) { + if ($document->getAttribute('confirm')) { // Count only confirmed members + $teamId = $document->getAttribute('teamId'); + $team = $dbForProject->getDocument('teams', $teamId); + if (!$team->isEmpty()) { + $team = $dbForProject->updateDocument( + 'teams', + $teamId, + // Ensure that total >= 0 + $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)) + ); + } + } + }); + + // Delete tokens + $this->deleteByGroup('tokens', [ + Query::equal('userId', [$userId]) + ], $dbForProject); + } + + /** + * @param database $dbForConsole + * @param callable $getProjectDB + * @param string $datetime + * @throws Exception + */ + protected function deleteExecutionLogs(database $dbForConsole, callable $getProjectDB, string $datetime): void + { + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $datetime) { + $dbForProject = $getProjectDB($project); + // Delete Executions + $this->deleteByGroup('executions', [ + Query::lessThan('$createdAt', $datetime) + ], $dbForProject); + }); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @return void + * @throws Exception|\Throwable + */ + protected function deleteExpiredSessions(Database $dbForConsole, callable $getProjectDB): void + { + + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($dbForConsole, $getProjectDB) { + $dbForProject = $getProjectDB($project); + $project = $dbForConsole->getDocument('projects', $project->getId()); + $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); + + // Delete Sessions + $this->deleteByGroup('sessions', [ + Query::lessThan('$createdAt', $expired) + ], $dbForProject); + }); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param string $datetime + * @throws Exception + */ + protected function deleteRealtimeUsage(Database $dbForConsole, callable $getProjectDB, string $datetime): void + { + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($datetime, $getProjectDB) { + $dbForProject = $getProjectDB($project); + // Delete Dead Realtime Logs + $this->deleteByGroup('realtime', [ + Query::lessThan('timestamp', $datetime) + ], $dbForProject); + }); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param string $datetime + * @throws Exception + */ + protected function deleteAbuseLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void + { + if (empty($datetime)) { + throw new Exception('Failed to delete audit logs. No datetime provided'); + } + + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $datetime) { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $timeLimit = new TimeLimit("", 0, 1, $dbForProject); + $abuse = new Abuse($timeLimit); + $status = $abuse->cleanup($datetime); + if (!$status) { + throw new Exception('Failed to delete Abuse logs for project ' . $projectId); + } + }); + } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param string $datetime + * @throws Exception + */ + protected function deleteAuditLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void + { + if (empty($datetime)) { + throw new Exception('Failed to delete audit logs. No datetime provided'); + } + + $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $datetime) { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $audit = new Audit($dbForProject); + $status = $audit->cleanup($datetime); + if (!$status) { + throw new Exception('Failed to delete Audit logs for project' . $projectId); + } + }); + } + + /** + * @param callable $getProjectDB + * @param string $resource + * @param Document $project + * @throws Exception + */ + protected function deleteAuditLogsByResource(callable $getProjectDB, string $resource, Document $project): void + { + $dbForProject = $getProjectDB($project); + + $this->deleteByGroup(Audit::COLLECTION, [ + Query::equal('resource', [$resource]) + ], $dbForProject); + } + + /** + * @param callable $getProjectDB + * @param callable $getFunctionsDevice + * @param callable $getBuildsDevice + * @param Document $document function document + * @param Document $project + * @throws Exception + */ + protected function deleteFunction(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $functionId = $document->getId(); + + /** + * Delete Variables + */ + Console::info("Deleting variables for function " . $functionId); + $this->deleteByGroup('variables', [ + Query::equal('functionId', [$functionId]) + ], $dbForProject); + + /** + * Delete Deployments + */ + Console::info("Deleting deployments for function " . $functionId); + $storageFunctions = $getFunctionsDevice($projectId); + $deploymentIds = []; + $this->deleteByGroup('deployments', [ + Query::equal('resourceId', [$functionId]) + ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) { + $deploymentIds[] = $document->getId(); + if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); + } else { + Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); + } + }); + + /** + * Delete builds + */ + Console::info("Deleting builds for function " . $functionId); + $storageBuilds = $getBuildsDevice($projectId); + foreach ($deploymentIds as $deploymentId) { + $this->deleteByGroup('builds', [ + Query::equal('deploymentId', [$deploymentId]) + ], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) { + if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); + } else { + Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); + } + }); + } + + /** + * Delete Executions + */ + Console::info("Deleting executions for function " . $functionId); + $this->deleteByGroup('executions', [ + Query::equal('functionId', [$functionId]) + ], $dbForProject); + + // TODO: Request executor to delete runtime + } + + /** + * @param callable $getProjectDB + * @param callable $getFunctionsDevice + * @param callable $getBuildsDevice + * @param Document $document deployment document + * @param Document $project + * @throws Exception + */ + protected function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $deploymentId = $document->getId(); + $functionId = $document->getAttribute('resourceId'); + + /** + * Delete deployment files + */ + Console::info("Deleting deployment files for deployment " . $deploymentId); + $storageFunctions = $getFunctionsDevice($projectId); + if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); + } else { + Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); + } + + /** + * Delete builds + */ + Console::info("Deleting builds for deployment " . $deploymentId); + $storageBuilds = $getBuildsDevice($projectId); + $this->deleteByGroup('builds', [ + Query::equal('deploymentId', [$deploymentId]) + ], $dbForProject, function (Document $document) use ($storageBuilds) { + if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); + } else { + Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); + } + }); + + // TODO: Request executor to delete runtime + } + + + /** + * @param Document $document to be deleted + * @param Database $database to delete it from + * @param callable|null $callback to perform after document is deleted + * @return bool + * @throws Authorization + */ + protected function deleteById(Document $document, Database $database, callable $callback = null): bool + { + if ($database->deleteDocument($document->getCollection(), $document->getId())) { + Console::success('Deleted document "' . $document->getId() . '" successfully'); + + if (is_callable($callback)) { + $callback($document); + } + + return true; + } else { + Console::error('Failed to delete document: ' . $document->getId()); + return false; + } + } + + /** + * @param Database $dbForConsole + * @param callable $callback + * @throws Exception + */ + protected function deleteForProjectIds(database $dbForConsole, callable $callback): void + { + // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document + $count = 0; + $chunk = 0; + $limit = 50; + $projects = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $projects = $dbForConsole->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); + + $chunk++; + + /** @var string[] $projectIds */ + $sum = count($projects); + + Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects'); + foreach ($projects as $project) { + $callback($project); + $count++; + } + } + + $executionEnd = \microtime(true); + Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); + } + + /** + * @param string $collection collectionID + * @param array $queries + * @param Database $database + * @param callable|null $callback + * @throws Exception + */ + protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void + { + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + + $sum = count($results); + + Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); + + foreach ($results as $document) { + $this->deleteById($document, $database, $callback); + $count++; + } + } + + $executionEnd = \microtime(true); + + Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + } + + /** + * @param string $collection collectionID + * @param Query[] $queries + * @param Database $database + * @param callable|null $callback + * @throws Exception + */ + protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void + { + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + + $sum = count($results); + + foreach ($results as $document) { + if (is_callable($callback)) { + $callback($document); + } + + $count++; + } + } + + $executionEnd = \microtime(true); + + Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + } + + /** + * @param Database $dbForConsole + * @param Document $document certificates document + * @throws Authorization + */ + protected function deleteCertificates(Database $dbForConsole, Document $document): void + { + // If domain has certificate generated + if (isset($document['certificateId'])) { + $domainUsingCertificate = $dbForConsole->findOne('domains', [ + Query::equal('certificateId', [$document['certificateId']]) + ]); + + if (!$domainUsingCertificate) { + $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); + if ($mainDomain === $document->getAttribute('domain')) { + $domainUsingCertificate = $mainDomain; + } + } + + // If certificate is still used by some domain, mark we can't delete. + // Current domain should not be found, because we only have copy. Original domain is already deleted from database. + if ($domainUsingCertificate) { + Console::warning("Skipping certificate deletion, because a domain is still using it."); + return; + } + } + + $domain = $document->getAttribute('domain'); + $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; + $checkTraversal = realpath($directory) === $directory; + + if ($domain && $checkTraversal && is_dir($directory)) { + // Delete certificate document, so Appwrite is aware of change + if (isset($document['certificateId'])) { + $dbForConsole->deleteDocument('certificates', $document['certificateId']); + } + + // Delete files, so Traefik is aware of change + array_map('unlink', glob($directory . '/*.*')); + rmdir($directory); + Console::info("Deleted certificate files for {$domain}"); + } else { + Console::info("No certificate files found for {$domain}"); + } + } + + /** + * @param callable $getProjectDB + * @param callable $getFilesDevice + * @param Document $document + * @param Document $project + * @return void + */ + protected function deleteBucket(callable $getProjectDB, callable $getFilesDevice, Document $document, Document $project): void + { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + + $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); + + $device = $getFilesDevice($projectId); + + $device->deletePath($document->getId()); + } +} diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php new file mode 100644 index 0000000000..9953560524 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -0,0 +1,350 @@ +desc('Functions worker') + ->inject('message') + ->inject('dbForProject') + ->inject('queueForFunctions') + ->inject('queueForUsage') + ->callback(fn($message, $dbForProject, $queueForFunctions, $queueForUsage) => $this->action($message, $dbForProject, $queueForFunctions, $queueForUsage)); + } + + /** + * @throws Exception|\Throwable + */ + public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $type = $payload['type'] ?? ''; + $events = $payload['events'] ?? []; + $data = $payload['data'] ?? ''; + $eventData = $payload['payload'] ?? ''; + $project = new Document($payload['project'] ?? []); + $function = new Document($payload['function'] ?? []); + $user = new Document($payload['user'] ?? []); + + if ($project->getId() === 'console') { + return; + } + + if (!empty($events)) { + $limit = 30; + $sum = 30; + $offset = 0; + $functions = []; + /** @var Document[] $functions */ + while ($sum >= $limit) { + $functions = $dbForProject->find('functions', [ + Query::limit($limit), + Query::offset($offset), + Query::orderAsc('name'), + ]); + + $sum = \count($functions); + $offset = $offset + $limit; + + Console::log('Fetched ' . $sum . ' functions...'); + + foreach ($functions as $function) { + if (!array_intersect($events, $function->getAttribute('events', []))) { + continue; + } + Console::success('Iterating function: ' . $function->getAttribute('name')); + + $this->execute( + dbForProject: $dbForProject, + queueForFunctions: $queueForFunctions, + queueForUsage: $queueForUsage, + project: $project, + function: $function, + trigger: 'event', + user: $user, + event: $events[0], + eventData: \is_string($eventData) ? $eventData : \json_encode($eventData), + ); + Console::success('Triggered function: ' . $events[0]); + } + } + return; + } + + /** + * Handle Schedule and HTTP execution. + */ + switch ($type) { + case 'http': + $jwt = $payload['jwt'] ?? ''; + $execution = new Document($payload['execution'] ?? []); + $user = new Document($payload['user'] ?? []); + $this->execute( + dbForProject: $dbForProject, + queueForFunctions: $queueForFunctions, + queueForUsage: $queueForUsage, + project: $project, + function: $function, + trigger: 'http', + data: $data, + user: $user, + jwt: $jwt, + executionId: $execution->getId(), + ); + break; + case 'schedule': + $this->execute( + dbForProject: $dbForProject, + queueForFunctions: $queueForFunctions, + queueForUsage: $queueForUsage, + project: $project, + function: $function, + trigger: 'schedule', + ); + break; + } + } + + /** + * @throws Authorization + * @throws \Throwable + * @throws Structure + */ + private function execute( + Database $dbForProject, + Func $queueForFunctions, + Usage $queueForUsage, + Document $project, + Document $function, + string $trigger, + string $data = null, + ?Document $user = null, + string $jwt = null, + string $event = null, + string $eventData = null, + string $executionId = null, + ): void { + $user ??= new Document(); + $functionId = $function->getId(); + $functionInternalId = $function->getInternalId(); + $deploymentId = $function->getAttribute('deployment', ''); + + /** Check if deployment exists */ + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + $deploymentInternalId = $deployment->getInternalId(); + + if ($deployment->getAttribute('resourceId') !== $functionId) { + throw new Exception('Deployment not found. Create deployment before trying to execute a function'); + } + + if ($deployment->isEmpty()) { + throw new Exception('Deployment not found. Create deployment before trying to execute a function'); + } + + /** Check if build has exists */ + $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); + if ($build->isEmpty()) { + throw new Exception('Build not found'); + } + + if ($build->getAttribute('status') !== 'ready') { + throw new Exception('Build not ready'); + } + + /** Check if runtime is supported */ + $runtimes = Config::getParam('runtimes', []); + + if (!\array_key_exists($function->getAttribute('runtime'), $runtimes)) { + throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); + } + + $runtime = $runtimes[$function->getAttribute('runtime')]; + + /** Create execution or update execution status */ + $execution = $dbForProject->getDocument('executions', $executionId ?? ''); + if ($execution->isEmpty()) { + $executionId = ID::unique(); + $execution = $dbForProject->createDocument('executions', new Document([ + '$id' => $executionId, + '$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))], + 'functionId' => $functionId, + 'functionInternalId' => $functionInternalId, + 'deploymentInternalId' => $deploymentInternalId, + 'deploymentId' => $deploymentId, + 'trigger' => $trigger, + 'status' => 'waiting', + 'statusCode' => 0, + 'response' => '', + 'stderr' => '', + 'duration' => 0.0, + 'search' => implode(' ', [$function->getId(), $executionId]), + ])); + + // TODO: @Meldiron Trigger executions.create event here + + if ($execution->isEmpty()) { + throw new Exception('Failed to create or read execution'); + } + + /*** Usage */ + $queueForUsage + ->addMetric(METRIC_EXECUTIONS, 1) // per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1); // per function + } + + $execution->setAttribute('status', 'processing'); + $execution = $dbForProject->updateDocument('executions', $executionId, $execution); + + $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { + $carry[$var->getAttribute('key')] = $var->getAttribute('value'); + return $carry; + }, []); + + /** Collect environment variables */ + $vars = \array_merge($vars, [ + 'APPWRITE_FUNCTION_ID' => $functionId, + 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), + 'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId, + 'APPWRITE_FUNCTION_TRIGGER' => $trigger, + 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', + 'APPWRITE_FUNCTION_EVENT' => $event ?? '', + 'APPWRITE_FUNCTION_EVENT_DATA' => $eventData ?? '', + 'APPWRITE_FUNCTION_DATA' => $data ?? '', + 'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '', + 'APPWRITE_FUNCTION_JWT' => $jwt ?? '', + ]); + + /** Execute function */ + try { + $client = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executionResponse = $client->createExecution( + projectId: $project->getId(), + deploymentId: $deploymentId, + payload: $vars['APPWRITE_FUNCTION_DATA'] ?? '', + variables: $vars, + timeout: $function->getAttribute('timeout', 0), + image: $runtime['image'], + source: $build->getAttribute('outputPath', ''), + entrypoint: $deployment->getAttribute('entrypoint', ''), + ); + + /** Update execution status */ + $execution + ->setAttribute('status', $executionResponse['status']) + ->setAttribute('statusCode', $executionResponse['statusCode']) + ->setAttribute('response', $executionResponse['response']) + ->setAttribute('stdout', $executionResponse['stdout']) + ->setAttribute('stderr', $executionResponse['stderr']) + ->setAttribute('duration', $executionResponse['duration']); + } catch (\Throwable $th) { + $interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt())); + $execution + ->setAttribute('duration', (float)$interval->format('%s.%f')) + ->setAttribute('status', 'failed') + ->setAttribute('statusCode', $th->getCode()) + ->setAttribute('stderr', $th->getMessage()); + + Console::error($th->getTraceAsString()); + Console::error($th->getFile()); + Console::error($th->getLine()); + Console::error($th->getMessage()); + } + + $execution = $dbForProject->updateDocument('executions', $executionId, $execution); + + /** Trigger Webhook */ + $executionModel = new Execution(); + $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); + $executionUpdate + ->setProject($project) + ->setUser($user) + ->setEvent('functions.[functionId].executions.[executionId].update') + ->setParam('functionId', $function->getId()) + ->setParam('executionId', $execution->getId()) + ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))) + ->trigger(); + + /** Trigger Functions */ + $queueForFunctions + ->from($executionUpdate) + ->trigger(); + + /** Trigger realtime event */ + $allEvents = Event::generateEvents('functions.[functionId].executions.[executionId].update', [ + 'functionId' => $function->getId(), + 'executionId' => $execution->getId() + ]); + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $execution + ); + Realtime::send( + projectId: 'console', + payload: $execution->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + Realtime::send( + projectId: $project->getId(), + payload: $execution->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + + /** Trigger usage queue */ + $queueForUsage + ->setProject($project) + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) + ->trigger() + ; + } +} From fd4f86b1c119b480b7ce7f7fbb8730ee845699bc Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 11 Jun 2023 13:29:04 +0300 Subject: [PATCH 032/144] functions/builds/deletes worker --- app/controllers/api/functions.php | 430 +++++++++----------- app/controllers/api/projects.php | 9 - app/worker.php | 54 ++- src/Appwrite/Platform/Workers/Builds.php | 30 +- src/Appwrite/Platform/Workers/Deletes.php | 80 ++-- src/Appwrite/Platform/Workers/Functions.php | 15 +- 6 files changed, 293 insertions(+), 325 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index d44c7082fe..4b3ccbc9b9 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -6,14 +6,14 @@ use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Func; +use Appwrite\Event\Usage; use Appwrite\Event\Validator\Event as ValidatorEvent; use Appwrite\Extend\Exception; use Appwrite\Utopia\Database\Validator\CustomId; -use Utopia\Database\ID; -use Utopia\Database\Permission; -use Utopia\Database\Role; +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; -use Appwrite\Usage\Stats; use Utopia\Storage\Device; use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileExt; @@ -58,7 +58,7 @@ App::post('/v1/functions') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_FUNCTION) - ->param('functionId', '', new CustomId(), 'Function ID. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('functionId', '', new CustomId(), 'Function ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Function name. Max length: 128 chars.') ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.') ->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.') @@ -81,10 +81,11 @@ App::post('/v1/functions') 'enabled' => $enabled, 'name' => $name, 'runtime' => $runtime, + 'deploymentInternalId' => '', 'deployment' => '', 'events' => $events, 'schedule' => $schedule, - 'scheduleUpdatedAt' => DateTime::now(), + 'scheduleInternalId' => '', 'timeout' => $timeout, 'search' => implode(' ', [$functionId, $name, $runtime]) ])); @@ -94,6 +95,7 @@ App::post('/v1/functions') 'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region 'resourceType' => 'function', 'resourceId' => $function->getId(), + 'resourceInternalId' => $function->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $function->getAttribute('schedule'), @@ -102,6 +104,7 @@ App::post('/v1/functions') ); $function->setAttribute('scheduleId', $schedule->getId()); + $function->setAttribute('scheduleInternalId', $schedule->getInternalId()); $dbForProject->updateDocument('functions', $function->getId(), $function); $queueForEvents->setParam('functionId', $function->getId()); @@ -230,92 +233,66 @@ App::get('/v1/functions/:functionId/usage') throw new Exception(Exception::FUNCTION_NOT_FOUND); } - $usage = []; - if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $periods = [ - '24h' => [ - 'period' => '1h', - 'limit' => 24, - ], - '7d' => [ - 'period' => '1d', - 'limit' => 7, - ], - '30d' => [ - 'period' => '1d', - 'limit' => 30, - ], - '90d' => [ - 'period' => '1d', - 'limit' => 90, - ], - ]; + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS), + str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), + str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), + str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), + str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), + str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), + str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), + ]; - $metrics = [ - "executions.$functionId.compute.total", - "executions.$functionId.compute.success", - "executions.$functionId.compute.failure", - "executions.$functionId.compute.time", - "builds.$functionId.compute.total", - "builds.$functionId.compute.success", - "builds.$functionId.compute.failure", - "builds.$functionId.compute.time", - ]; - - $stats = []; - - Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $periods[$range]['limit']; - $period = $periods[$range]['period']; - - $requestDocs = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - // backfill metrics with empty values for graphs - $backfill = $limit - \count($requestDocs); - while ($backfill > 0) { - $last = $limit - $backfill - 1; // array index of last added metric - $diff = match ($period) { // convert period to seconds for unix timestamp math - '1h' => 3600, - '1d' => 86400, - }; - $stats[$metric][] = [ - 'value' => 0, - 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), - ]; - $backfill--; - } - $stats[$metric] = array_reverse($stats[$metric]); + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric] = []; + foreach ($results as $result) { + $stats[$metric][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; } - }); + } + }); - $usage = new Document([ - 'range' => $range, - 'executionsTotal' => $stats["executions.$functionId.compute.total"] ?? [], - 'executionsFailure' => $stats["executions.$functionId.compute.failure"] ?? [], - 'executionsSuccesse' => $stats["executions.$functionId.compute.success"] ?? [], - 'executionsTime' => $stats["executions.$functionId.compute.time"] ?? [], - 'buildsTotal' => $stats["builds.$functionId.compute.total"] ?? [], - 'buildsFailure' => $stats["builds.$functionId.compute.failure"] ?? [], - 'buildsSuccess' => $stats["builds.$functionId.compute.success"] ?? [], - 'buildsTime' => $stats["builds.$functionId.compute.time" ?? []] - ]); + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric][] = [ + 'value' => $stats[$metric][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; } + } - $response->dynamic($usage, Response::MODEL_USAGE_FUNCTION); + $response->dynamic(new Document([ + 'range' => $range, + 'deploymentsTotal' => $usage[$metrics[0]], + 'deploymentsStorage' => $usage[$metrics[1]], + 'buildsTotal' => $usage[$metrics[2]], + 'buildsStorage' => $usage[$metrics[3]], + 'buildsTime' => $usage[$metrics[4]], + 'executionsTotal' => $usage[$metrics[5]], + 'executionsTime' => $usage[$metrics[6]], + ]), Response::MODEL_USAGE_FUNCTION); }); App::get('/v1/functions/usage') @@ -333,92 +310,67 @@ App::get('/v1/functions/usage') ->inject('dbForProject') ->action(function (string $range, Response $response, Database $dbForProject) { - $usage = []; - if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $periods = [ - '24h' => [ - 'period' => '1h', - 'limit' => 24, - ], - '7d' => [ - 'period' => '1d', - 'limit' => 7, - ], - '30d' => [ - 'period' => '1d', - 'limit' => 30, - ], - '90d' => [ - 'period' => '1d', - 'limit' => 90, - ], - ]; + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + METRIC_FUNCTIONS, + METRIC_DEPLOYMENTS, + METRIC_DEPLOYMENTS_STORAGE, + METRIC_BUILDS, + METRIC_BUILDS_STORAGE, + METRIC_BUILDS_COMPUTE, + METRIC_EXECUTIONS, + METRIC_EXECUTIONS_COMPUTE, + ]; - $metrics = [ - 'executions.$all.compute.total', - 'executions.$all.compute.failure', - 'executions.$all.compute.success', - 'executions.$all.compute.time', - 'builds.$all.compute.total', - 'builds.$all.compute.failure', - 'builds.$all.compute.success', - 'builds.$all.compute.time', - ]; - - $stats = []; - - Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $periods[$range]['limit']; - $period = $periods[$range]['period']; - - $requestDocs = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - // backfill metrics with empty values for graphs - $backfill = $limit - \count($requestDocs); - while ($backfill > 0) { - $last = $limit - $backfill - 1; // array index of last added metric - $diff = match ($period) { // convert period to seconds for unix timestamp math - '1h' => 3600, - '1d' => 86400, - }; - $stats[$metric][] = [ - 'value' => 0, - 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), - ]; - $backfill--; - } - $stats[$metric] = array_reverse($stats[$metric]); + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric] = []; + foreach ($results as $result) { + $stats[$metric][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; } - }); + } + }); - $usage = new Document([ - 'range' => $range, - 'executionsTotal' => $stats[$metrics[0]] ?? [], - 'executionsFailure' => $stats[$metrics[1]] ?? [], - 'executionsSuccess' => $stats[$metrics[2]] ?? [], - 'executionsTime' => $stats[$metrics[3]] ?? [], - 'buildsTotal' => $stats[$metrics[4]] ?? [], - 'buildsFailure' => $stats[$metrics[5]] ?? [], - 'buildsSuccess' => $stats[$metrics[6]] ?? [], - 'buildsTime' => $stats[$metrics[7]] ?? [], - ]); + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric][] = [ + 'value' => $stats[$metric][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; } - - $response->dynamic($usage, Response::MODEL_USAGE_FUNCTIONS); + } + $response->dynamic(new Document([ + 'range' => $range, + 'functionsTotal' => $usage[$metrics[0]], + 'deploymentsTotal' => $usage[$metrics[1]], + 'deploymentsStorage' => $usage[$metrics[2]], + 'buildsTotal' => $usage[$metrics[3]], + 'buildsStorage' => $usage[$metrics[4]], + 'buildsTime' => $usage[$metrics[5]], + 'executionsTotal' => $usage[$metrics[6]], + 'executionsTime' => $usage[$metrics[7]], + ]), Response::MODEL_USAGE_FUNCTIONS); }); App::put('/v1/functions/:functionId') @@ -463,26 +415,16 @@ App::put('/v1/functions/:functionId') 'name' => $name, 'events' => $events, 'schedule' => $schedule, - 'scheduleUpdatedAt' => DateTime::now(), 'timeout' => $timeout, 'enabled' => $enabled, 'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]), ]))); $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - - /** - * In case we want to clear the schedule - */ - if (!empty($function->getAttribute('deployment'))) { - $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); - } - $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents->setParam('functionId', $function->getId()); @@ -534,19 +476,15 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') } $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ - 'deployment' => $deployment->getId() + 'deploymentInternalId' => $deployment->getInternalId(), + 'deployment' => $deployment->getId(), ]))); $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - - $active = !empty($function->getAttribute('schedule')); - - if ($active) { - $schedule->setAttribute('resourceUpdatedAt', datetime::now()); - } - - $schedule->setAttribute('active', $active); - + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents @@ -589,12 +527,9 @@ App::delete('/v1/functions/:functionId') } $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule ->setAttribute('resourceUpdatedAt', DateTime::now()) - ->setAttribute('active', false) - ; - + ->setAttribute('active', false); Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForDeletes @@ -644,14 +579,20 @@ App::post('/v1/functions/:functionId/deployments') } $file = $request->getFiles('code'); - $fileExt = new FileExt([FileExt::TYPE_GZIP]); - $fileSizeValidator = new FileSize(App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', 0)); - $upload = new Upload(); + + // GraphQL multipart spec adds files with index keys + if (empty($file)) { + $file = $request->getFiles(0); + } if (empty($file)) { throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent'); } + $fileExt = new FileExt([FileExt::TYPE_GZIP]); + $fileSizeValidator = new FileSize(App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', 0)); + $upload = new Upload(); + // Make sure we handle a single file and multiple files the same way $fileName = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name']; $fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name']; @@ -696,7 +637,6 @@ App::post('/v1/functions/:functionId/deployments') // Save to storage $fileSize ??= $deviceLocal->getFileSize($fileTmpName); $path = $deviceFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION)); - $deployment = $dbForProject->getDocument('deployments', $deploymentId); $metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)]; @@ -741,8 +681,10 @@ App::post('/v1/functions/:functionId/deployments') Permission::update(Role::any()), Permission::delete(Role::any()), ], + 'resourceInternalId' => $function->getInternalId(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', + 'buildInternalId' => '', 'entrypoint' => $entrypoint, 'path' => $path, 'size' => $fileSize, @@ -770,8 +712,10 @@ App::post('/v1/functions/:functionId/deployments') Permission::update(Role::any()), Permission::delete(Role::any()), ], + 'resourceInternalId' => $function->getInternalId(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', + 'buildInternalId' => '', 'entrypoint' => $entrypoint, 'path' => $path, 'size' => $fileSize, @@ -786,22 +730,6 @@ App::post('/v1/functions/:functionId/deployments') } } - /** - * TODO Should we update also the function collection with the scheduleUpdatedAt attr? - */ - - $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - - $active = !empty($function->getAttribute('schedule')); - - if ($active) { - $schedule->setAttribute('resourceUpdatedAt', datetime::now()); - } - - $schedule->setAttribute('active', $active); - - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - $metadata = null; $queueForEvents @@ -1063,10 +991,10 @@ App::post('/v1/functions/:functionId/executions') ->inject('dbForProject') ->inject('user') ->inject('queueForEvents') - ->inject('usage') ->inject('mode') ->inject('queueForFunctions') - ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Stats $usage, string $mode, Func $queueForFunctions) { + ->inject('queueForUsage') + ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Func $queueForFunctions, Usage $queueForUsage) { $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); @@ -1116,7 +1044,9 @@ App::post('/v1/functions/:functionId/executions') $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', new Document([ '$id' => $executionId, '$permissions' => !$user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [], + 'functionInternalId' => $function->getInternalId(), 'functionId' => $function->getId(), + 'deploymentInternalId' => $deployment->getInternalId(), 'deploymentId' => $deployment->getId(), 'trigger' => 'http', // http / schedule / event 'status' => $async ? 'waiting' : 'processing', // waiting / processing / completed / failed @@ -1197,7 +1127,7 @@ App::post('/v1/functions/:functionId/executions') variables: $vars, timeout: $function->getAttribute('timeout', 0), image: $runtime['image'], - source: $build->getAttribute('path', ''), + source: $build->getAttribute('outputPath', ''), entrypoint: $deployment->getAttribute('entrypoint', ''), ); @@ -1208,6 +1138,13 @@ App::post('/v1/functions/:functionId/executions') $execution->setAttribute('stdout', $executionResponse['stdout']); $execution->setAttribute('stderr', $executionResponse['stderr']); $execution->setAttribute('duration', $executionResponse['duration']); + /** + * Sync execution compute usage from + */ + $queueForUsage + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function + ; } catch (\Throwable $th) { $interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt())); $execution @@ -1220,13 +1157,6 @@ App::post('/v1/functions/:functionId/executions') Authorization::skip(fn () => $dbForProject->updateDocument('executions', $executionId, $execution)); - // TODO revise this later using route label - $usage - ->setParam('functionId', $function->getId()) - ->setParam('executions.{scope}.compute', 1) - ->setParam('executionStatus', $execution->getAttribute('status', '')) - ->setParam('executionTime', $execution->getAttribute('duration')); // ms - $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1381,7 +1311,8 @@ App::post('/v1/functions/:functionId/variables') ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject) { + ->inject('dbForConsole') + ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1410,8 +1341,22 @@ App::post('/v1/functions/:functionId/variables') throw new Exception(Exception::VARIABLE_ALREADY_EXISTS); } + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $dbForProject->deleteCachedDocument('functions', $function->getId()); + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($variable, Response::MODEL_VARIABLE); @@ -1497,7 +1442,8 @@ App::put('/v1/functions/:functionId/variables/:variableId') ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject) { + ->inject('dbForConsole') + ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1526,8 +1472,22 @@ App::put('/v1/functions/:functionId/variables/:variableId') throw new Exception(Exception::VARIABLE_ALREADY_EXISTS); } + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $dbForProject->deleteCachedDocument('functions', $function->getId()); + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $response->dynamic($variable, Response::MODEL_VARIABLE); }); @@ -1547,7 +1507,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId') ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) { + ->inject('dbForConsole') + ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1564,7 +1525,22 @@ App::delete('/v1/functions/:functionId/variables/:variableId') } $dbForProject->deleteDocument('variables', $variable->getId()); + + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $dbForProject->deleteCachedDocument('functions', $function->getId()); + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $response->noContent(); }); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 30db3db9fa..f40b75d938 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -207,7 +207,6 @@ App::get('/v1/projects') $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); $cursor = reset($cursor); if ($cursor) { - /** @var Query $cursor */ $projectId = $cursor->getValue(); $cursorDocument = $dbForConsole->getDocument('projects', $projectId); @@ -538,8 +537,6 @@ App::delete('/v1/projects/:projectId') $response->noContent(); }); -// Webhooks - App::post('/v1/projects/:projectId/webhooks') ->desc('Create Webhook') ->groups(['api', 'projects']) @@ -792,8 +789,6 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') $response->noContent(); }); -// Keys - App::post('/v1/projects/:projectId/keys') ->desc('Create Key') ->groups(['api', 'projects']) @@ -994,8 +989,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $response->noContent(); }); -// Platforms - App::post('/v1/projects/:projectId/platforms') ->desc('Create Platform') ->groups(['api', 'projects']) @@ -1197,8 +1190,6 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') $response->noContent(); }); -// Domains - App::post('/v1/projects/:projectId/domains') ->desc('Create Domain') ->groups(['api', 'projects']) diff --git a/app/worker.php b/app/worker.php index 08c38ebd8b..b99d08db7c 100644 --- a/app/worker.php +++ b/app/worker.php @@ -160,38 +160,15 @@ Server::setResource('pools', function ($register) { return $register->get('pools'); }, ['register']); -/** - * Get Console DB - * - * @returns Cache - */ -function getCache(): Cache -{ - global $register; - - $pools = $register->get('pools'); - /** @var Group $pools */ - - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); - } - - return new Cache(new Sharding($adapters)); -} - /** * Get Functions Storage Device * @param string $projectId of the project * @return Device */ -Server::setResource('getFunctionsDevice', function (string $projectId) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); +Server::setResource('deviceFunctions', function () { + return function (string $projectId) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); + }; }); /** @@ -199,8 +176,10 @@ Server::setResource('getFunctionsDevice', function (string $projectId) { * @param string $projectId of the project * @return Device */ -Server::setResource('getFilesDevice', function (string $projectId) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); +Server::setResource('deviceFiles', function () { + return function (string $projectId) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); + }; }); /** @@ -208,8 +187,21 @@ Server::setResource('getFilesDevice', function (string $projectId) { * @param string $projectId of the project * @return Device */ -Server::setResource('getBuildsDevice', function (string $projectId) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); +Server::setResource('deviceBuilds', function () { + return function (string $projectId) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); + }; +}); + +/** + * Get cache Device + * @param string $projectId of the project + * @return Device + */ +Server::setResource('deviceCache', function () { + return function (string $projectId) { + return getDevice(APP_STORAGE_CACHE . '/app-' . $projectId); + }; }); $pools = $register->get('pools'); diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 93c0bc7589..7dbdc9b67f 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -38,17 +38,18 @@ class Builds extends Action $this ->desc('Builds worker') ->inject('message') + ->inject('dbForConsole') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForFunctions') ->inject('queueForUsage') - ->callback(fn($message, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage) => $this->action($message, $dbForProject, $queueForEvents, $queueForFunctions, $queueForUsage)); + ->callback(fn($message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage) => $this->action($message, $dbForConsole, $dbForProject, $queueForEvents, $queueForFunctions, $queueForUsage)); } /** * @throws Exception|\Throwable */ - public function action(Message $message, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage): void + public function action(Message $message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage): void { $payload = $message->getPayload() ?? []; @@ -66,6 +67,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $this->buildDeployment( + dbForConsole: $dbForConsole, dbForProject: $dbForProject, queueForEvents: $queueForEvents, queueForFunctions: $queueForFunctions, @@ -86,7 +88,7 @@ class Builds extends Action * @throws \Throwable * @throws Structure */ - private function buildDeployment(Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Document $deployment, Document $project, Document $function): void + private function buildDeployment(Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Document $deployment, Document $project, Document $function): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); $function = $dbForProject->getDocument('functions', $function->getId()); @@ -126,10 +128,10 @@ class Builds extends Action '$id' => $buildId, '$permissions' => [], 'startTime' => $startTime, + 'deploymentInternalId' => $deployment->getInternalId(), 'deploymentId' => $deployment->getId(), 'status' => 'processing', - 'path' => '', - 'size' => 0, + 'outputPath' => '', 'runtime' => $function->getAttribute('runtime'), 'source' => $deployment->getAttribute('path'), 'sourceType' => $device, @@ -139,6 +141,7 @@ class Builds extends Action ])); $deployment->setAttribute('buildId', $buildId); + $deployment->setAttribute('buildInternalId', $build->getInternalId()); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); } else { $build = $dbForProject->getDocument('builds', $buildId); @@ -211,16 +214,18 @@ class Builds extends Action commands: [ 'sh', '-c', 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ - cd /usr/local/src/ && ./build.sh' + cd /usr/local/src/ && ./build.sh' ] ); + $endTime = new \DateTime(); + $endTime->setTimestamp($response['endTimeUnix']); + /** Update the build document */ - $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); + $build->setAttribute('endTime', DateTime::format($endTime)); $build->setAttribute('duration', \intval($response['duration'])); $build->setAttribute('status', $response['status']); - $build->setAttribute('path', $response['path']); - $build->setAttribute('size', $response['size']); + $build->setAttribute('outputPath', $response['outputPath']); $build->setAttribute('stderr', $response['stderr']); $build->setAttribute('stdout', $response['stdout']); @@ -229,18 +234,16 @@ class Builds extends Action Console::success("Build id: $buildId created"); - $function->setAttribute('scheduleUpdatedAt', DateTime::now()); - /** Set auto deploy */ if ($deployment->getAttribute('activate') === true) { + $function->setAttribute('deploymentInternalId', $deployment->getInternalId()); $function->setAttribute('deployment', $deployment->getId()); $function = $dbForProject->updateDocument('functions', $function->getId(), $function); } /** Update function schedule */ - $dbForConsole = getConsoleDB(); $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); + $schedule->setAttribute('resourceUpdatedAt', DateTime::now()); $schedule ->setAttribute('schedule', $function->getAttribute('schedule')) @@ -251,6 +254,7 @@ class Builds extends Action } catch (\Throwable $th) { $endTime = DateTime::now(); $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); + $build->setAttribute('endTime', $endTime); $build->setAttribute('duration', $interval->format('%s') + 0); $build->setAttribute('status', 'failed'); $build->setAttribute('stderr', $th->getMessage()); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index bf53c396c3..4a56a0ddb6 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -35,18 +35,18 @@ class Deletes extends Action ->inject('message') ->inject('dbForConsole') ->inject('getProjectDB') - ->inject('getFilesDevice') - ->inject('getFunctionsDevice') - ->inject('getBuildsDevice') - ->inject('getCacheDevice') - ->callback(fn($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice) => $this->action($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice)); + ->inject('deviceFiles') + ->inject('deviceFunctions') + ->inject('deviceBuilds') + ->inject('deviceCache') + ->callback(fn($message, $dbForConsole, $getProjectDB, $deviceFiles, $deviceFunctions, $deviceBuilds, $deviceCache) => $this->action($message, $dbForConsole, $getProjectDB, $deviceFiles, $deviceFunctions, $deviceBuilds, $deviceCache)); } /** * @throws Exception * @throws \Throwable */ - public function action(Message $message, Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice): void + public function action(Message $message, Database $dbForConsole, callable $getProjectDB, callable $deviceFiles, callable $deviceFunctions, callable $deviceBuilds, callable $deviceCache): void { $payload = $message->getPayload() ?? []; @@ -71,13 +71,13 @@ class Deletes extends Action $this->deleteCollection($getProjectDB, $document, $project); break; case DELETE_TYPE_PROJECTS: - $this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document); + $this->deleteProject($dbForConsole, $getProjectDB, $deviceFiles, $deviceFunctions, $deviceBuilds, $deviceCache, $document); break; case DELETE_TYPE_FUNCTIONS: - $this->deleteFunction($getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); + $this->deleteFunction($getProjectDB, $deviceFunctions, $deviceBuilds, $document, $project); break; case DELETE_TYPE_DEPLOYMENTS: - $this->deleteDeployment($getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); + $this->deleteDeployment($getProjectDB, $deviceFunctions, $deviceBuilds, $document, $project); break; case DELETE_TYPE_USERS: $this->deleteUser($getProjectDB, $document, $project); @@ -86,7 +86,7 @@ class Deletes extends Action $this->deleteMemberships($getProjectDB, $document, $project); break; case DELETE_TYPE_BUCKETS: - $this->deleteBucket($getProjectDB, $getFilesDevice, $document, $project); + $this->deleteBucket($getProjectDB, $deviceFiles, $document, $project); break; default: if (\str_starts_with($document->getCollection(), 'database_')) { @@ -134,10 +134,10 @@ class Deletes extends Action break; case DELETE_TYPE_CACHE_BY_RESOURCE: - $this->deleteCacheByResource($getProjectDB, $resource); + $this->deleteCacheByResource($dbForConsole, $getProjectDB, $resource); break; case DELETE_TYPE_CACHE_BY_TIMESTAMP: - $this->deleteCacheByDate($getProjectDB); + $this->deleteCacheByDate($dbForConsole, $getProjectDB, $datetime); break; case DELETE_TYPE_SCHEDULES: $this->deleteSchedules($dbForConsole, $getProjectDB, $datetime); @@ -190,9 +190,9 @@ class Deletes extends Action * @param string $resource * @throws Exception */ - protected function deleteCacheByResource(callable $getProjectDB, string $resource): void + protected function deleteCacheByResource(Database $dbForConsole, callable $getProjectDB, string $resource): void { - $this->deleteCacheFiles($getProjectDB, [ + $this->deleteCacheFiles($dbForConsole, $getProjectDB, [ Query::equal('resource', [$resource]), ]); } @@ -200,10 +200,10 @@ class Deletes extends Action /** * @throws Exception */ - protected function deleteCacheByDate(callable $getProjectDB): void + protected function deleteCacheByDate(Database $dbForConsole, callable $getProjectDB, string $datetime): void { - $this->deleteCacheFiles($getProjectDB, [ - Query::lessThan('accessedAt', $this->args['datetime']), + $this->deleteCacheFiles($dbForConsole, $getProjectDB, [ + Query::lessThan('accessedAt', $datetime), ]); } @@ -323,14 +323,14 @@ class Deletes extends Action /** * @param Database $dbForConsole * @param callable $getProjectDB - * @param callable $getFilesDevice - * @param callable $getFunctionsDevice - * @param callable $getBuildsDevice - * @param callable $getCacheDevice + * @param callable $deviceFiles + * @param callable $deviceFunctions + * @param callable $deviceBuilds + * @param callable $deviceCache * @param Document $document project document * @throws Authorization */ - protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void + protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $deviceFiles, callable $deviceFunctions, callable $deviceBuilds, callable $deviceCache, Document $document): void { $projectId = $document->getId(); @@ -344,7 +344,7 @@ class Deletes extends Action } // Delete project tables - $dbForProject = $getProjectDB($projectId, $document); + $dbForProject = $getProjectDB($document); while (true) { $collections = $dbForProject->listCollections(); @@ -367,10 +367,10 @@ class Deletes extends Action } // Delete all storage directories - $uploads = $getFilesDevice($projectId); - $functions = $getFunctionsDevice($projectId); - $builds = $getBuildsDevice($projectId); - $cache = $getCacheDevice($projectId); + $uploads = $deviceFiles($projectId); + $functions = $deviceFunctions($projectId); + $builds = $deviceBuilds($projectId); + $cache = $deviceCache($projectId); $uploads->delete($uploads->getRoot(), true); $functions->delete($functions->getRoot(), true); @@ -540,13 +540,13 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $getFunctionsDevice - * @param callable $getBuildsDevice + * @param callable $deviceFunctions + * @param callable $deviceBuilds * @param Document $document function document * @param Document $project * @throws Exception */ - protected function deleteFunction(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + protected function deleteFunction(callable $getProjectDB, callable $deviceFunctions, callable $deviceBuilds, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -564,7 +564,7 @@ class Deletes extends Action * Delete Deployments */ Console::info("Deleting deployments for function " . $functionId); - $storageFunctions = $getFunctionsDevice($projectId); + $storageFunctions = $deviceFunctions($projectId); $deploymentIds = []; $this->deleteByGroup('deployments', [ Query::equal('resourceId', [$functionId]) @@ -581,7 +581,7 @@ class Deletes extends Action * Delete builds */ Console::info("Deleting builds for function " . $functionId); - $storageBuilds = $getBuildsDevice($projectId); + $storageBuilds = $deviceBuilds($projectId); foreach ($deploymentIds as $deploymentId) { $this->deleteByGroup('builds', [ Query::equal('deploymentId', [$deploymentId]) @@ -607,13 +607,13 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $getFunctionsDevice - * @param callable $getBuildsDevice + * @param callable $deviceFunctions + * @param callable $deviceBuilds * @param Document $document deployment document * @param Document $project * @throws Exception */ - protected function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + protected function deleteDeployment(callable $getProjectDB, callable $deviceFunctions, callable $deviceBuilds, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -624,7 +624,7 @@ class Deletes extends Action * Delete deployment files */ Console::info("Deleting deployment files for deployment " . $deploymentId); - $storageFunctions = $getFunctionsDevice($projectId); + $storageFunctions = $deviceFunctions($projectId); if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); } else { @@ -635,7 +635,7 @@ class Deletes extends Action * Delete builds */ Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $getBuildsDevice($projectId); + $storageBuilds = $deviceBuilds($projectId); $this->deleteByGroup('builds', [ Query::equal('deploymentId', [$deploymentId]) ], $dbForProject, function (Document $document) use ($storageBuilds) { @@ -832,19 +832,19 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $getFilesDevice + * @param callable $deviceFiles * @param Document $document * @param Document $project * @return void */ - protected function deleteBucket(callable $getProjectDB, callable $getFilesDevice, Document $document, Document $project): void + protected function deleteBucket(callable $getProjectDB, callable $deviceFiles, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); - $device = $getFilesDevice($projectId); + $device = $deviceFiles($projectId); $device->deletePath($document->getId()); } diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 9953560524..6e57d9d49a 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -40,14 +40,15 @@ class Functions extends Action ->inject('message') ->inject('dbForProject') ->inject('queueForFunctions') + ->inject('queueForEvents') ->inject('queueForUsage') - ->callback(fn($message, $dbForProject, $queueForFunctions, $queueForUsage) => $this->action($message, $dbForProject, $queueForFunctions, $queueForUsage)); + ->callback(fn($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage)); } /** * @throws Exception|\Throwable */ - public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage): void + public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage): void { $payload = $message->getPayload() ?? []; @@ -100,6 +101,7 @@ class Functions extends Action $this->execute( dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, + queueForEvents: $queueForEvents, queueForUsage: $queueForUsage, project: $project, function: $function, @@ -125,6 +127,7 @@ class Functions extends Action $this->execute( dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, + queueForEvents: $queueForEvents, queueForUsage: $queueForUsage, project: $project, function: $function, @@ -156,6 +159,7 @@ class Functions extends Action private function execute( Database $dbForProject, Func $queueForFunctions, + Event $queueForEvents, Usage $queueForUsage, Document $project, Document $function, @@ -299,8 +303,9 @@ class Functions extends Action /** Trigger Webhook */ $executionModel = new Execution(); - $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); - $executionUpdate + $queueForEvents + ->setQueue(Event::WEBHOOK_QUEUE_NAME) + ->setClass(Event::WEBHOOK_CLASS_NAME) ->setProject($project) ->setUser($user) ->setEvent('functions.[functionId].executions.[executionId].update') @@ -311,7 +316,7 @@ class Functions extends Action /** Trigger Functions */ $queueForFunctions - ->from($executionUpdate) + ->from($queueForEvents) ->trigger(); /** Trigger realtime event */ From 57bc7d05847462b283e9afd1dbe49e5e37fd9704 Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 11 Jun 2023 17:08:48 +0300 Subject: [PATCH 033/144] functions/builds/deletes worker --- .env | 2 +- app/cli.php | 2 +- app/controllers/api/account.php | 24 +++++++++---------- app/controllers/api/functions.php | 1 + app/controllers/api/teams.php | 6 ++--- app/controllers/shared/api.php | 6 ++--- app/init.php | 2 +- app/worker.php | 2 +- app/workers/certificates.php | 8 +++---- docker-compose.yml | 1 + .../Platform/Workers/Certificates.php | 18 +++++++------- src/Appwrite/Platform/Workers/Deletes.php | 4 ++-- src/Appwrite/Platform/Workers/Functions.php | 1 + src/Appwrite/Platform/Workers/Webhooks.php | 3 ++- 14 files changed, 41 insertions(+), 39 deletions(-) diff --git a/.env b/.env index fff40e5f71..73f4dd0720 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -_APP_ENV=development +_APP_ENV=production _APP_LOCALE=en _APP_WORKER_PER_CORE=2 _APP_CONSOLE_WHITELIST_ROOT=disabled diff --git a/app/cli.php b/app/cli.php index 7beab28715..99ff82d325 100644 --- a/app/cli.php +++ b/app/cli.php @@ -132,7 +132,7 @@ CLI::setResource('queue', function (Group $pools) { CLI::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); -CLI::setResource('queueForMail', function (Connection $queue) { +CLI::setResource('queueForMails', function (Connection $queue) { return new Mail($queue); }, ['queue']); CLI::setResource('queueForBuilds', function (Connection $queue) { diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 7040496ac7..a275e0aa78 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -732,8 +732,8 @@ App::post('/v1/account/sessions/magic-url') ->inject('dbForProject') ->inject('locale') ->inject('queueForEvents') - ->inject('queueForMail') - ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMail) { + ->inject('queueForMails') + ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -837,13 +837,11 @@ App::post('/v1/account/sessions/magic-url') $body = $body->render(); - $queueForMail + $queueForMails ->setSubject($subject) ->setBody($body) ->setFrom($from) ->setRecipient($user->getAttribute('email')) - ->setUrl($url) - ->setLocale($locale->default) ->trigger() ; @@ -1624,6 +1622,8 @@ App::patch('/v1/account/password') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') ->param('password', '', new Password(), 'New user password. Must be at least 8 chars.') ->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true) ->inject('response') @@ -2055,9 +2055,9 @@ App::post('/v1/account/recovery') ->inject('dbForProject') ->inject('project') ->inject('locale') - ->inject('queueForMail') + ->inject('queueForMails') ->inject('queueForEvents') - ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMail, Event $queueForEvents) { + ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -2133,7 +2133,7 @@ App::post('/v1/account/recovery') $body = $body->render(); - $queueForMail + $queueForMails ->setRecipient($profile->getAttribute('email', '')) ->setName($profile->getAttribute('name')) ->setBody($body) @@ -2252,8 +2252,8 @@ App::post('/v1/account/verification') ->inject('dbForProject') ->inject('locale') ->inject('queueForEvents') - ->inject('queueForMail') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMail) { + ->inject('queueForMails') + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -2312,13 +2312,11 @@ App::post('/v1/account/verification') $body = $body->render(); - $queueForMail + $queueForMails ->setSubject($subject) ->setBody($body) ->setFrom($from) ->setRecipient($user->getAttribute('email')) - ->setUrl($url) - ->setLocale($locale->default) ->setName($user->getAttribute('name')) ->trigger() ; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 4b3ccbc9b9..c208286231 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -895,6 +895,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') if ($function->getAttribute('deployment') === $deployment->getId()) { // Reset function deployment $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ 'deployment' => '', + 'deploymentInternalId' => '', ]))); } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index a5682c6112..6e0baa9003 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -315,9 +315,9 @@ App::post('/v1/teams/:teamId/memberships') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('queueForMail') + ->inject('queueForMails') ->inject('queueForEvents') - ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMail, Event $queueForEvents) { + ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Event $queueForEvents) { $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); @@ -461,7 +461,7 @@ App::post('/v1/teams/:teamId/memberships') $body = $body->render(); - $queueForMail + $queueForMails ->setSubject($subject) ->setBody($body) ->setFrom($from) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 76c985002b..69e729418d 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -156,13 +156,13 @@ App::init() ->inject('user') ->inject('queueForEvents') ->inject('queueForAudits') - ->inject('queueForMail') + ->inject('queueForMails') ->inject('queueForDeletes') ->inject('queueForDatabase') ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMail, Delete $queueForDeletes, EventDatabase $queueForDatabase, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Mail $queueForMails, Delete $queueForDeletes, EventDatabase $queueForDatabase, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { $route = $utopia->match($request); @@ -236,7 +236,7 @@ App::init() ->setProject($project) ->setUser($user); - $queueForMail + $queueForMails ->setProject($project) ->setUser($user); diff --git a/app/init.php b/app/init.php index 7ded34755b..0b09e7dd78 100644 --- a/app/init.php +++ b/app/init.php @@ -869,7 +869,7 @@ App::setResource('queue', function (Group $pools) { App::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); -App::setResource('queueForMail', function (Connection $queue) { +App::setResource('queueForMails', function (Connection $queue) { return new Mail($queue); }, ['queue']); App::setResource('queueForBuilds', function (Connection $queue) { diff --git a/app/worker.php b/app/worker.php index b99d08db7c..26dedf49df 100644 --- a/app/worker.php +++ b/app/worker.php @@ -125,7 +125,7 @@ Server::setResource('queueForDatabase', function (Connection $queue) { Server::setResource('queueForMessaging', function (Connection $queue) { return new Phone($queue); }, ['queue']); -Server::setResource('queueForMail', function (Connection $queue) { +Server::setResource('queueForMails', function (Connection $queue) { return new Mail($queue); }, ['queue']); Server::setResource('queueForBuilds', function (Connection $queue) { diff --git a/app/workers/certificates.php b/app/workers/certificates.php index adf207ef37..8377a261df 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -383,17 +383,17 @@ Server::setResource('applyCertificateFiles', function () { * @param int $attempt How many times it failed already * @return void */ -Server::setResource('notifyError', function (Mail $queueForMail) { +Server::setResource('notifyError', function (Mail $queueForMails) { return function ( string $domain, string $errorMessage, int $attempt, - ) use ($queueForMail) { + ) use ($queueForMails) { // Log error into console Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); // Send mail to administratore mail - $queueForMail + $queueForMails ->setType(MAIL_TYPE_CERTIFICATE) ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) ->setUrl('https://' . $domain) @@ -406,7 +406,7 @@ Server::setResource('notifyError', function (Mail $queueForMail) { ]) ->trigger(); }; -}, ['queueForMail']); +}, ['queueForMails']); /** * Update all existing domain documents so they have relation to correct certificate document. diff --git a/docker-compose.yml b/docker-compose.yml index 59baa08ba9..00ef90f1d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -308,6 +308,7 @@ services: - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/storage:/usr/src/code/vendor/utopia-php/storage environment: - _APP_ENV - _APP_WORKER_PER_CORE diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 6e1d1010c1..df60343078 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -35,14 +35,14 @@ class Certificates extends Action ->desc('Certificates worker') ->inject('message') ->inject('dbForConsole') - ->inject('queueForMail') - ->callback(fn($message, $dbForConsole, $queueForMail) => $this->action($message, $dbForConsole, $queueForMail)); + ->inject('queueForMails') + ->callback(fn($message, $dbForConsole, $queueForMails) => $this->action($message, $dbForConsole, $queueForMails)); } /** * @throws Exception|Throwable */ - public function action(Message $message, Database $dbForConsole, Mail $queueForMail): void + public function action(Message $message, Database $dbForConsole, Mail $queueForMails): void { $payload = $message->getPayload() ?? []; @@ -54,13 +54,13 @@ class Certificates extends Action $domain = new Domain($document->getAttribute('domain', '')); $skipRenewCheck = $payload['skipRenewCheck'] ?? false; - $this->execute($domain, $dbForConsole, $queueForMail, $skipRenewCheck); + $this->execute($domain, $dbForConsole, $queueForMails, $skipRenewCheck); } /** * @throws Exception|Throwable */ - private function execute(Domain $domain, Database $dbForConsole, Mail $queueForMail, bool $skipRenewCheck = false): void + private function execute(Domain $domain, Database $dbForConsole, Mail $queueForMails, bool $skipRenewCheck = false): void { /** * 1. Read arguments and validate domain @@ -152,7 +152,7 @@ class Certificates extends Action $certificate->setAttribute('renewDate', DateTime::now()); // Send email to security email - $this->notifyError($domain->get(), $e->getMessage(), $attempts, $queueForMail); + $this->notifyError($domain->get(), $e->getMessage(), $attempts, $queueForMails); } finally { // All actions result in new updatedAt date $certificate->setAttribute('updated', DateTime::now()); @@ -340,11 +340,11 @@ class Certificates extends Action * @param string $domain Domain that caused the error * @param string $errorMessage Verbose error message * @param int $attempt How many times it failed already - * @param Mail $queueForMail + * @param Mail $queueForMails * @return void * @throws Exception */ - private function notifyError(string $domain, string $errorMessage, int $attempt, Mail $queueForMail): void + private function notifyError(string $domain, string $errorMessage, int $attempt, Mail $queueForMails): void { // Log error into console Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); @@ -375,7 +375,7 @@ class Certificates extends Action ->setParam('{{bg-content}}', '#ffffff') ->setParam('{{text-content}}', '#000000'); - $queueForMail + $queueForMails ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) ->setBody($body->render()) ->setName('Appwrite Administrator') diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 4a56a0ddb6..3c036d182f 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -59,7 +59,7 @@ class Deletes extends Action $hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null; $resource = $payload['resource'] ?? null; $document = new Document($payload['document'] ?? []); - $project = new Document($payload['project'] ?? []); + $project = new Document($payload['project'] ?? []); switch (strval($type)) { case DELETE_TYPE_DOCUMENT: @@ -186,6 +186,7 @@ class Deletes extends Action } /** + * @param Database $dbForConsole * @param callable $getProjectDB * @param string $resource * @throws Exception @@ -618,7 +619,6 @@ class Deletes extends Action $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $deploymentId = $document->getId(); - $functionId = $document->getAttribute('resourceId'); /** * Delete deployment files diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 6e57d9d49a..d3c4399323 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -142,6 +142,7 @@ class Functions extends Action $this->execute( dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, + queueForEvents: $queueForEvents, queueForUsage: $queueForUsage, project: $project, function: $function, diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index fb40ffb8fe..85a8e1eae0 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -60,13 +60,14 @@ class Webhooks extends Action private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void { + $url = \rawurldecode($webhook->getAttribute('url')); $signatureKey = $webhook->getAttribute('signatureKey'); $signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true)); $httpUser = $webhook->getAttribute('httpUser'); $httpPass = $webhook->getAttribute('httpPass'); $ch = \curl_init($webhook->getAttribute('url')); - + var_dump($url); \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); \curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); \curl_setopt($ch, CURLOPT_HEADER, 0); From 785c00ae5c595773c8346a141d9175c1ec84ab06 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 12 Jun 2023 13:04:47 +0300 Subject: [PATCH 034/144] functions/builds/deletes worker --- composer.lock | 5216 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5216 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..1a049d84cd --- /dev/null +++ b/composer.lock @@ -0,0 +1,5216 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "185f3be3c459767318599669f63145af", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", + "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" + }, + "time": "2020-10-02T05:23:46+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.11.1", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.6.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2022-11-07T16:45:52+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.3", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", + "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2021-11-25T22:38:09+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-01-18T15:43:28+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "1e0104b46f045868f11942aea058cd7186d6c303" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", + "reference": "1e0104b46f045868f11942aea058cd7186d6c303", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.8.0", + "php": "^7.0|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0|^8.5|^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A wrapper for ocramius/package-versions to get pretty versions strings", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" + }, + "time": "2021-02-04T16:20:16+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", + "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2022-04-11T09:58:17+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "953dbc19443aa9314c44b7217a16873347e6840d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", + "reference": "953dbc19443aa9314c44b7217a16873347e6840d", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.8.1", + "jean85/pretty-package-versions": "^1.2", + "php": "^7.0 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.5, <3.5.5", + "symfony/phpunit-bridge": "5.x-dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" + }, + "time": "2020-11-25T12:26:02+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.6.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", + "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.2", + "php-parallel-lint/php-console-highlighter": "^0.5.0", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.6.2", + "yoast/phpunit-polyfills": "^1.0.0" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2022-02-28T15:31:21+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.18.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", + "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.30.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.4", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.18.0" + }, + "time": "2023-02-14T09:56:04+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.20.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", + "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.30.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.20.0" + }, + "time": "2023-02-14T09:46:54+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.30.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", + "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.0.2" + }, + "require-dev": { + "ext-mongodb": "*", + "ext-redis": "*", + "fakerphp/faker": "^1.14", + "mongodb/mongodb": "1.8.0", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistency using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.30.1" + }, + "time": "2023-02-14T06:25:03+00:00" + }, + { + "name": "utopia-php/domains", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/master" + }, + "time": "2020-02-23T07:40:02+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.26.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", + "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.5.25", + "vimeo/psalm": "4.27.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.26.3" + }, + "time": "2023-06-03T14:01:15+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + }, + "time": "2023-02-07T05:42:46+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.0.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", + "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.8.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.0.2" + }, + "time": "2022-11-08T11:58:46+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "dev-integrate-workers", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "a7f33f0f2f427b946cac8120f26dc52eff84e928" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/a7f33f0f2f427b946cac8120f26dc52eff84e928", + "reference": "a7f33f0f2f427b946cac8120f26dc52eff84e928", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.26.*", + "utopia-php/queue": "0.5.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/integrate-workers" + }, + "time": "2023-06-05T08:44:03+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "0.5.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/0.5.3" + }, + "time": "2023-05-24T19:06:04+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.14.0" + }, + "time": "2023-03-15T00:16:34+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.6.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "289c4327713deadc9c748b5317d248133a02f245" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", + "reference": "289c4327713deadc9c748b5317d248133a02f245", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.6.0" + }, + "time": "2022-11-07T13:51:59+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.9", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70", + "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0", + "squizlabs/php_codesniffer": "3.5.4" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.9" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-01-06T12:12:50+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.29.4", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", + "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" + }, + "time": "2023-02-03T05:44:59+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + }, + "time": "2023-06-03T09:27:29+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.70", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b", + "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.70" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2022-12-09T12:56:44+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" + }, + "time": "2023-05-19T20:20:00+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" + }, + "time": "2023-05-30T18:13:47+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.22.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", + "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.0" + }, + "time": "2023-06-01T12:35:21+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-03-06T12:58:08+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "4.8.9", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", + "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/4.8.9" + }, + "funding": [ + { + "url": "https://gitee.com/swoole/swoole?donate=true", + "type": "custom" + }, + { + "url": "https://github.com/swoole", + "type": "github" + } + ], + "time": "2022-04-18T20:38:04+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "106c170d08e8415d78be2d16c3d057d0d108262b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/106c170d08e8415d78be2d16c3d057d0d108262b", + "reference": "106c170d08e8415d78be2d16c3d057d0d108262b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-05-03T19:06:57+00:00" + } + ], + "aliases": [ + { + "package": "utopia-php/platform", + "version": "dev-integrate-workers", + "alias": "0.3.3", + "alias_normalized": "0.3.3.0" + } + ], + "minimum-stability": "stable", + "stability-flags": { + "utopia-php/platform": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From 89f0146c4552c814733c54aab368bc976a426f2d Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 10 Jul 2023 11:03:59 +0300 Subject: [PATCH 035/144] users addition --- app/console | 2 +- app/worker.php | 5 +- bin/worker-usage | 2 +- composer.lock | 5216 ------------------- docker-compose.yml | 2 + src/Appwrite/Platform/Services/Workers.php | 5 +- src/Appwrite/Platform/Workers/Usage.php | 23 +- src/Appwrite/Platform/Workers/UsageHook.php | 106 + 8 files changed, 126 insertions(+), 5235 deletions(-) delete mode 100644 composer.lock create mode 100644 src/Appwrite/Platform/Workers/UsageHook.php diff --git a/app/console b/app/console index 9174d8f8cb..b981302dee 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 9174d8f8cb584744dd7a53f69d324f490ee82ee3 +Subproject commit b981302dee30eab33e155af79f0088822b29a2b6 diff --git a/app/worker.php b/app/worker.php index 26dedf49df..d0a53bdb53 100644 --- a/app/worker.php +++ b/app/worker.php @@ -274,8 +274,5 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); -$worker->workerStart() - ->action(function () use ($workerName) { - Console::info("Worker $workerName started"); - }); + $worker->start(); diff --git a/bin/worker-usage b/bin/worker-usage index 9d325ac46e..e39ce8477c 100644 --- a/bin/worker-usage +++ b/bin/worker-usage @@ -1,3 +1,3 @@ #!/bin/sh -QUEUE=v1-usage php /usr/src/code/app/workers/usage.php $@ \ No newline at end of file +php /usr/src/code/app/worker.php usage $@ \ No newline at end of file diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 1a049d84cd..0000000000 --- a/composer.lock +++ /dev/null @@ -1,5216 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "185f3be3c459767318599669f63145af", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" - }, - "time": "2020-10-02T05:23:46+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.11.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.6.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2022-11-07T16:45:52+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.3", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2021-11-25T22:38:09+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.1", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-01-18T15:43:28+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303", - "shasum": "" - }, - "require": { - "composer/package-versions-deprecated": "^1.8.0", - "php": "^7.0|^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0|^8.5|^9.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A wrapper for ocramius/package-versions to get pretty versions strings", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" - }, - "time": "2021-02-04T16:20:16+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2022-04-11T09:58:17+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.8.1", - "jean85/pretty-package-versions": "^1.2", - "php": "^7.0 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "squizlabs/php_codesniffer": "^3.5, <3.5.5", - "symfony/phpunit-bridge": "5.x-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" - }, - "time": "2020-11-25T12:26:02+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.6.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.2", - "php-parallel-lint/php-console-highlighter": "^0.5.0", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.6.2", - "yoast/phpunit-polyfills": "^1.0.0" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2022-02-28T15:31:21+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.18.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.4", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.18.0" - }, - "time": "2023-02-14T09:56:04+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.20.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "shasum": "" - }, - "require": { - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.20.0" - }, - "time": "2023-02-14T09:46:54+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.30.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.0.2" - }, - "require-dev": { - "ext-mongodb": "*", - "ext-redis": "*", - "fakerphp/faker": "^1.14", - "mongodb/mongodb": "1.8.0", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistency using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.30.1" - }, - "time": "2023-02-14T06:25:03+00:00" - }, - { - "name": "utopia-php/domains", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/master" - }, - "time": "2020-02-23T07:40:02+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.26.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", - "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.5.25", - "vimeo/psalm": "4.27.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.26.3" - }, - "time": "2023-06-03T14:01:15+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" - }, - "time": "2023-02-07T05:42:46+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.0.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.8.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.0.2" - }, - "time": "2022-11-08T11:58:46+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "dev-integrate-workers", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "a7f33f0f2f427b946cac8120f26dc52eff84e928" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/a7f33f0f2f427b946cac8120f26dc52eff84e928", - "reference": "a7f33f0f2f427b946cac8120f26dc52eff84e928", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.26.*", - "utopia-php/queue": "0.5.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/integrate-workers" - }, - "time": "2023-06-05T08:44:03+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "0.5.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.3" - }, - "time": "2023-05-24T19:06:04+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.14.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" - }, - "time": "2023-03-15T00:16:34+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.6.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "289c4327713deadc9c748b5317d248133a02f245" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", - "reference": "289c4327713deadc9c748b5317d248133a02f245", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.6.0" - }, - "time": "2022-11-07T13:51:59+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.9", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70", - "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0", - "squizlabs/php_codesniffer": "3.5.4" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.9" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-01-06T12:12:50+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.29.4", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" - }, - "time": "2023-02-03T05:44:59+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" - }, - "time": "2023-06-03T09:27:29+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.70", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b", - "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.70" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2022-12-09T12:56:44+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.15.5", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" - }, - "time": "2023-05-19T20:20:00+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" - }, - "time": "2023-05-30T18:13:47+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.22.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", - "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.0" - }, - "time": "2023-06-01T12:35:21+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.26", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-03-06T12:58:08+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-02-14T08:28:10+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "4.8.9", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/4.8.9" - }, - "funding": [ - { - "url": "https://gitee.com/swoole/swoole?donate=true", - "type": "custom" - }, - { - "url": "https://github.com/swoole", - "type": "github" - } - ], - "time": "2022-04-18T20:38:04+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "106c170d08e8415d78be2d16c3d057d0d108262b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/106c170d08e8415d78be2d16c3d057d0d108262b", - "reference": "106c170d08e8415d78be2d16c3d057d0d108262b", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-05-03T19:06:57+00:00" - } - ], - "aliases": [ - { - "package": "utopia-php/platform", - "version": "dev-integrate-workers", - "alias": "0.3.3", - "alias_normalized": "0.3.3.0" - } - ], - "minimum-stability": "stable", - "stability-flags": { - "utopia-php/platform": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.2.0" -} diff --git a/docker-compose.yml b/docker-compose.yml index 00ef90f1d9..43de4ea876 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -636,6 +636,8 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform + depends_on: - redis - mariadb diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 6ad47e32bb..e93b74ce98 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -13,6 +13,7 @@ use Appwrite\Platform\Workers\Functions; use Appwrite\Platform\Workers\Builds; use Appwrite\Platform\Workers\Deletes; use Appwrite\Platform\Workers\Usage; +use Appwrite\Platform\Workers\UsageHook; class Workers extends Service { @@ -29,7 +30,9 @@ class Workers extends Service ->addAction(Functions::getName(), new Functions()) ->addAction(Builds::getName(), new Builds()) ->addAction(Deletes::getName(), new Deletes()) - //->addAction(Usage::getName(), new Usage()) + ->addAction(UsageHook::getName(), new UsageHook()) + ->addAction(Usage::getName(), new Usage()) + ; } } diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php index 7546cdf9bc..5487a1c8b4 100644 --- a/src/Appwrite/Platform/Workers/Usage.php +++ b/src/Appwrite/Platform/Workers/Usage.php @@ -11,17 +11,15 @@ use Utopia\Queue\Message; class Usage extends Action { - private $stats = []; - private array $periods = [ - '1h' => 'Y-m-d H:00', - '1d' => 'Y-m-d 00:00', - 'inf' => '0000-00-00 00:00' + protected static array $stats = []; + protected array $periods = [ + '1h' => 'Y-m-d H:00', + '1d' => 'Y-m-d 00:00', + 'inf' => '0000-00-00 00:00' ]; - const INFINITY_PERIOD = '_inf_'; - public static function getName(): string { return 'usage'; @@ -32,12 +30,13 @@ class Usage extends Action */ public function __construct() { + $this ->desc('Usage worker') ->inject('message') ->inject('pools') ->inject('cache') - ->callback(function ($message, $pools, $cache) use (&$stats) { + ->callback(function ($message, $pools, $cache) { $this->action($message, $pools, $cache); }); } @@ -71,13 +70,13 @@ class Usage extends Action ); } - $stats[$projectId]['database'] = $project->getAttribute('database'); + self::$stats[$projectId]['database'] = $project->getAttribute('database'); foreach ($payload['metrics'] ?? [] as $metric) { - if (!isset($stats[$projectId]['keys'][$metric['key']])) { - $stats[$projectId]['keys'][$metric['key']] = $metric['value']; + if (!isset(self::$stats[$projectId]['keys'][$metric['key']])) { + self::$stats[$projectId]['keys'][$metric['key']] = $metric['value']; continue; } - $stats[$projectId]['keys'][$metric['key']] += $metric['value']; + self::$stats[$projectId]['keys'][$metric['key']] += $metric['value']; } } diff --git a/src/Appwrite/Platform/Workers/UsageHook.php b/src/Appwrite/Platform/Workers/UsageHook.php new file mode 100644 index 0000000000..06d5cb9feb --- /dev/null +++ b/src/Appwrite/Platform/Workers/UsageHook.php @@ -0,0 +1,106 @@ +setType(Action::TYPE_WORKER_START) + ->inject('register') + ->inject('cache') + ->inject('pools') + ->callback(function ($register, $cache, $pools) { + $this->action($register, $cache, $pools); + }) + ; + } + + public function action($register, $cache, $pools): void + { + Timer::tick(30000, function () use ($register, $cache, $pools) { + + $offset = count(self::$stats); + $projects = array_slice(self::$stats, 0, $offset, true); + array_splice(self::$stats, 0, $offset); + + foreach ($projects as $projectInternalId => $project) { + try { + $dbForProject = new Database( + $pools + ->get($project['database']) + ->pop() + ->getResource(), + $cache + ); + + $dbForProject->setNamespace('_' . $projectInternalId); + + foreach ($project['keys'] ?? [] as $key => $value) { + if ($value == 0) { + continue; + } + + foreach ($this->periods as $period => $format) { + $time = 'inf' === $period ? null : date($format, time()); + $id = \md5("{$time}_{$period}_{$key}"); + + try { + $dbForProject->createDocument('stats', new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $key, + 'value' => $value, + 'region' => App::getEnv('_APP_REGION', 'default'), + ])); + } catch (Duplicate $th) { + if ($value < 0) { + $dbForProject->decreaseDocumentAttribute( + 'stats', + $id, + 'value', + abs($value) + ); + } else { + $dbForProject->increaseDocumentAttribute( + 'stats', + $id, + 'value', + $value + ); + } + } + } + } + if (!empty($project['keys'])) { + $dbForProject->createDocument('statsLogger', new Document([ + 'time' => DateTime::now(), + 'metrics' => $project['keys'], + ])); + } + } catch (\Exception $e) { + console::error("[logger] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}"); + } finally { + $pools->reclaim(); + } + } + }); + } +} From 5366d90ff2bb7ee757bdc87e98584a7ba5c0075b Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 10 Jul 2023 11:07:54 +0300 Subject: [PATCH 036/144] users addition --- .env | 2 +- app/worker.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 73f4dd0720..fff40e5f71 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -_APP_ENV=production +_APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=2 _APP_CONSOLE_WHITELIST_ROOT=disabled diff --git a/app/worker.php b/app/worker.php index d0a53bdb53..e17fa67c03 100644 --- a/app/worker.php +++ b/app/worker.php @@ -274,5 +274,12 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); +// We need to add `getWorkerStartHook` in `utopia-php/queue` so that we can check if +// we have already added workerStart hook, if not we can add this default worker start hook here + +// $worker->workerStart() +// ->action(function () use ($workerName) { +// Console::info("Worker $workerName started"); +// }); $worker->start(); From 1358d6c455deade9fe59bdda9b9aaadf1fd2eda1 Mon Sep 17 00:00:00 2001 From: Shimon Newman Date: Mon, 10 Jul 2023 11:13:35 +0300 Subject: [PATCH 037/144] Update src/Appwrite/Platform/Workers/Audits.php Co-authored-by: Damodar Lohani --- src/Appwrite/Platform/Workers/Audits.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index bd299b7b0b..f889f5e939 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -33,7 +33,7 @@ class Audits extends Action /** * @throws Exception */ - public function action(Message $message, $dbForProject): void + public function action(Message $message, Database $dbForProject): void { $payload = $message->getPayload() ?? []; From e95094517022ef1468382ceb5b913c7691266436 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 11 Jul 2023 18:46:21 +0300 Subject: [PATCH 038/144] users addition --- app/cli.php | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/app/cli.php b/app/cli.php index 99ff82d325..9ef0826a05 100644 --- a/app/cli.php +++ b/app/cli.php @@ -129,30 +129,10 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); -CLI::setResource('queueForMessaging', function (Connection $queue) { - return new Phone($queue); -}, ['queue']); -CLI::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -CLI::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -CLI::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); + CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); -CLI::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -CLI::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -CLI::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); CLI::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); From 58aa29a4ae02bc8ebf01b0dc2fc4035294bde2e3 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 11 Jul 2023 18:47:38 +0300 Subject: [PATCH 039/144] cli --- composer.lock | 5215 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5215 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..ffa819df8f --- /dev/null +++ b/composer.lock @@ -0,0 +1,5215 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "185f3be3c459767318599669f63145af", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", + "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" + }, + "time": "2020-10-02T05:23:46+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.11.1", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.6.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2022-11-07T16:45:52+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.3", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", + "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2021-11-25T22:38:09+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-01-18T15:43:28+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "1e0104b46f045868f11942aea058cd7186d6c303" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", + "reference": "1e0104b46f045868f11942aea058cd7186d6c303", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.8.0", + "php": "^7.0|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0|^8.5|^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A wrapper for ocramius/package-versions to get pretty versions strings", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" + }, + "time": "2021-02-04T16:20:16+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", + "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2022-04-11T09:58:17+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "953dbc19443aa9314c44b7217a16873347e6840d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", + "reference": "953dbc19443aa9314c44b7217a16873347e6840d", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.8.1", + "jean85/pretty-package-versions": "^1.2", + "php": "^7.0 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.5, <3.5.5", + "symfony/phpunit-bridge": "5.x-dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" + }, + "time": "2020-11-25T12:26:02+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.6.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", + "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.2", + "php-parallel-lint/php-console-highlighter": "^0.5.0", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.6.2", + "yoast/phpunit-polyfills": "^1.0.0" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2022-02-28T15:31:21+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.18.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", + "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.30.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.4", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.18.0" + }, + "time": "2023-02-14T09:56:04+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.20.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", + "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.30.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.20.0" + }, + "time": "2023-02-14T09:46:54+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.30.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", + "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.0.2" + }, + "require-dev": { + "ext-mongodb": "*", + "ext-redis": "*", + "fakerphp/faker": "^1.14", + "mongodb/mongodb": "1.8.0", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistency using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.30.1" + }, + "time": "2023-02-14T06:25:03+00:00" + }, + { + "name": "utopia-php/domains", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/master" + }, + "time": "2020-02-23T07:40:02+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.26.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", + "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.5.25", + "vimeo/psalm": "4.27.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.26.3" + }, + "time": "2023-06-03T14:01:15+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + }, + "time": "2023-02-07T05:42:46+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.0.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", + "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.8.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.0.2" + }, + "time": "2022-11-08T11:58:46+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "dev-integrate-workers", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "9a3fe2451a815353faf8d12f620b688b39e1f13f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/9a3fe2451a815353faf8d12f620b688b39e1f13f", + "reference": "9a3fe2451a815353faf8d12f620b688b39e1f13f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.26.*", + "utopia-php/queue": "0.5.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/integrate-workers" + }, + "time": "2023-07-09T15:14:30+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "0.5.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/0.5.3" + }, + "time": "2023-05-24T19:06:04+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.14.0" + }, + "time": "2023-03-15T00:16:34+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.6.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "289c4327713deadc9c748b5317d248133a02f245" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", + "reference": "289c4327713deadc9c748b5317d248133a02f245", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.6.0" + }, + "time": "2022-11-07T13:51:59+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.10", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-07-05T14:23:37+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.29.4", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", + "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" + }, + "time": "2023-02-03T05:44:59+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + }, + "time": "2023-06-03T09:27:29+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.71", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2023-04-25T20:33:03+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.16.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "19526a33fb561ef417e822e85f08a00db4059c17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", + "reference": "19526a33fb561ef417e822e85f08a00db4059c17", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" + }, + "time": "2023-06-25T14:52:30+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" + }, + "time": "2023-05-30T18:13:47+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.22.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" + }, + "time": "2023-06-29T20:46:06+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-03-06T12:58:08+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "4.8.9", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", + "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/4.8.9" + }, + "funding": [ + { + "url": "https://gitee.com/swoole/swoole?donate=true", + "type": "custom" + }, + { + "url": "https://github.com/swoole", + "type": "github" + } + ], + "time": "2022-04-18T20:38:04+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", + "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.6.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-06-08T12:52:13+00:00" + } + ], + "aliases": [ + { + "package": "utopia-php/platform", + "version": "dev-integrate-workers", + "alias": "0.3.3", + "alias_normalized": "0.3.3.0" + } + ], + "minimum-stability": "stable", + "stability-flags": { + "utopia-php/platform": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From d1d1392bb3257aeaaf9430984b0f22e11c13ca2f Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 12 Jul 2023 11:12:56 +0300 Subject: [PATCH 040/144] small adjustments --- app/cli.php | 8 -------- app/worker.php | 19 ++++++++++--------- composer.json | 2 +- composer.lock | 21 ++++++++++++++------- docker-compose.yml | 3 +++ 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/app/cli.php b/app/cli.php index 9ef0826a05..fb9bae476e 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,13 +5,6 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Delete; use Appwrite\Event\Certificate; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Func; -use Appwrite\Event\Audit; -use Appwrite\Event\Build; -use Appwrite\Event\Event; -use Appwrite\Event\Mail; -use Appwrite\Event\Phone; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; use Utopia\Database\Validator\Authorization; @@ -26,7 +19,6 @@ use Utopia\Database\Document; use Utopia\Logger\Log; use Utopia\Pools\Group; use Utopia\Queue\Connection; -use Utopia\Queue\Server; use Utopia\Registry\Registry; Authorization::disable(); diff --git a/app/worker.php b/app/worker.php index e17fa67c03..ff67e49205 100644 --- a/app/worker.php +++ b/app/worker.php @@ -152,11 +152,11 @@ Server::setResource('queueForCertificates', function (Connection $queue) { Server::setResource('queueForUsage', function (Connection $queue) { return new Usage($queue); }, ['queue']); -Server::setResource('logger', function ($register) { +Server::setResource('logger', function (Registry $register) { return $register->get('logger'); }, ['register']); -Server::setResource('pools', function ($register) { +Server::setResource('pools', function (Registry $register) { return $register->get('pools'); }, ['register']); @@ -274,12 +274,13 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); -// We need to add `getWorkerStartHook` in `utopia-php/queue` so that we can check if -// we have already added workerStart hook, if not we can add this default worker start hook here +try { + $workerStart = $worker->getWorkerStart(); +} catch (\Throwable $error) { + $worker->workerStart() + ->action(function () use ($workerName) { + Console::info("Worker $workerName started"); + }); +} - -// $worker->workerStart() -// ->action(function () use ($workerName) { -// Console::info("Worker $workerName started"); -// }); $worker->start(); diff --git a/composer.json b/composer.json index 0a3abc121a..a8aa34e991 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.30.*", - "utopia-php/queue": "0.5.*", + "utopia-php/queue": "dev-feat-get-worker-start as 0.5.3", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "dev-integrate-workers as 0.3.3", "utopia-php/pools": "0.4.*", diff --git a/composer.lock b/composer.lock index ffa819df8f..44edb1f0fe 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": "185f3be3c459767318599669f63145af", + "content-hash": "2cf09045bf1484d009c4af490ea4adba", "packages": [ { "name": "adhocore/jwt", @@ -2100,16 +2100,16 @@ }, { "name": "utopia-php/queue", - "version": "0.5.3", + "version": "dev-feat-get-worker-start", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" + "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", + "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", "shasum": "" }, "require": { @@ -2155,9 +2155,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.3" + "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" }, - "time": "2023-05-24T19:06:04+00:00" + "time": "2023-07-12T07:55:31+00:00" }, { "name": "utopia-php/registry", @@ -5182,10 +5182,17 @@ "version": "dev-integrate-workers", "alias": "0.3.3", "alias_normalized": "0.3.3.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-get-worker-start", + "alias": "0.5.3", + "alias_normalized": "0.5.3.0" } ], "minimum-stability": "stable", "stability-flags": { + "utopia-php/queue": 20, "utopia-php/platform": 20 }, "prefer-stable": false, diff --git a/docker-compose.yml b/docker-compose.yml index 43de4ea876..844177101d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -379,6 +379,8 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform + - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue depends_on: - redis - mariadb @@ -637,6 +639,7 @@ services: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform + - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue depends_on: - redis From d28a235b4d1a53bb2f7a2c1ca53d50a366b9b982 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 17 Jul 2023 16:31:19 +0300 Subject: [PATCH 041/144] $projectInternalId rollback to false --- app/config/collections.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/collections.php b/app/config/collections.php index 12904a1e20..2ef81ceb18 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -986,7 +986,7 @@ $collections = [ 'format' => '', 'size' => Database::LENGTH_KEY, 'signed' => true, - 'required' => true, + 'required' => false, 'default' => null, 'array' => false, 'filters' => [], From 1fd7f0c5d713f141cfff7fc50fae070a9f6bc76f Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 18 Jul 2023 10:54:11 +0300 Subject: [PATCH 042/144] SSl task --- .env | 4 ++-- src/Appwrite/Platform/Tasks/SSL.php | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.env b/.env index fff40e5f71..351f2548dc 100644 --- a/.env +++ b/.env @@ -44,8 +44,8 @@ _APP_SMTP_PASSWORD= _APP_HAMSTER_INTERVAL=86400 _APP_HAMSTER_TIME=21:00 _APP_MIXPANEL_TOKEN= -_APP_SMS_PROVIDER=sms://username:password@mock -_APP_SMS_FROM=+123456789 +_APP_SMS_PROVIDER=sms://5fccf626:sFYCY5Q2Z7zTbsPK@vonage +_APP_SMS_FROM=+972504368300 _APP_STORAGE_LIMIT=30000000 _APP_STORAGE_PREVIEW_LIMIT=20000000 _APP_FUNCTIONS_SIZE_LIMIT=30000000 diff --git a/src/Appwrite/Platform/Tasks/SSL.php b/src/Appwrite/Platform/Tasks/SSL.php index 43026b0753..6dbf4dcd70 100644 --- a/src/Appwrite/Platform/Tasks/SSL.php +++ b/src/Appwrite/Platform/Tasks/SSL.php @@ -21,14 +21,15 @@ class SSL extends Action $this ->desc('Validate server certificates') ->param('domain', App::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true) - ->callback(fn ($domain) => $this->action($domain)); + ->inject('queueForCertificates') + ->callback(fn (string $domain, Certificate $queueForCertificates) => $this->action($domain, $queueForCertificates)); } - public function action(string $domain): void + public function action(string $domain, Certificate $queueForCertificates): void { Console::success('Scheduling a job to issue a TLS certificate for domain: ' . $domain); - (new Certificate()) + $queueForCertificates ->setDomain(new Document([ 'domain' => $domain ])) From dcf0107a79f3aba95114f5714d478f16cdbf4cf1 Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 27 Sep 2023 20:10:21 +0300 Subject: [PATCH 043/144] sync with master --- Dockerfile | 1 + app/controllers/api/account.php | 38 +- app/controllers/api/databases.php | 1 - app/controllers/api/functions.php | 44 +- app/controllers/api/users.php | 7 +- composer.json | 1 - composer.lock | 5222 ----------------- src/Appwrite/Platform/Services/Tasks.php | 2 - src/Appwrite/Platform/Tasks/Usage.php | 60 - src/Appwrite/Usage/Calculator.php | 15 - src/Appwrite/Usage/Calculators/TimeSeries.php | 557 -- src/Appwrite/Usage/Stats.php | 225 - .../Database/Validator/Queries/Base.php | 1 - 13 files changed, 42 insertions(+), 6132 deletions(-) delete mode 100644 composer.lock delete mode 100644 src/Appwrite/Platform/Tasks/Usage.php delete mode 100644 src/Appwrite/Usage/Calculator.php delete mode 100644 src/Appwrite/Usage/Calculators/TimeSeries.php delete mode 100644 src/Appwrite/Usage/Stats.php diff --git a/Dockerfile b/Dockerfile index 8cea481543..dd13341dcc 100755 --- a/Dockerfile +++ b/Dockerfile @@ -94,6 +94,7 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/worker-mails && \ chmod +x /usr/local/bin/worker-messaging && \ chmod +x /usr/local/bin/worker-webhooks && \ + chmod +x /usr/local/bin/worker-usage && \ chmod +x /usr/local/bin/worker-migrations # Cloud Executabless diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index d9e20e38ad..ed4cc6c4a3 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -192,7 +192,7 @@ App::post('/v1/account/sessions/email') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $email, string $password, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -356,10 +356,10 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) + ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'Login state params.', true) - ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) - ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) + ->param('error', '', new Text(2048), 'Error code returned from the OAuth2 provider.', true) + ->param('error_description', '', new Text(2048), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) ->inject('request') ->inject('response') ->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) { @@ -389,10 +389,10 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) + ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'Login state params.', true) - ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) - ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) + ->param('error', '', new Text(2048,), 'Error code returned from the OAuth2 provider.', true) + ->param('error_description', '', new Text(2048), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) ->inject('request') ->inject('response') ->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) { @@ -426,10 +426,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('abuse-key', 'ip:{ip}') ->label('docs', false) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) + ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'OAuth2 state params.', true) - ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) - ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) + ->param('error', '', new Text(2048), 'Error code returned from the OAuth2 provider.', true) + ->param('error_description', '', new Text(2048), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) ->inject('request') ->inject('response') ->inject('project') @@ -893,7 +893,7 @@ App::post('/v1/account/sessions/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -1014,7 +1014,7 @@ App::post('/v1/account/sessions/magic-url') $replyTo = $smtp['replyTo']; } - $queueForMails + $queueForMails ->setSmtpHost($smtp['host'] ?? '') ->setSmtpPort($smtp['port'] ?? '') ->setSmtpUsername($smtp['username'] ?? '') @@ -1036,7 +1036,7 @@ App::post('/v1/account/sessions/magic-url') $subject = $customTemplate['subject'] ?? $subject; } - $queueForMails + $queueForMails ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); @@ -1230,7 +1230,7 @@ App::post('/v1/account/sessions/phone') ->inject('queueForEvents') ->inject('queueForMessaging') ->inject('locale') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $project, Database $dbForProject, Event $queueForEvents, EventPhone $queueForMessaging, Locale $locale) { + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, EventPhone $queueForMessaging, Locale $locale) { if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); @@ -1363,9 +1363,7 @@ App::put('/v1/account/sessions/phone') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { - ->inject('events') - ->action(function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $events) { + ->action(function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { $userFromRequest = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -2073,7 +2071,7 @@ App::patch('/v1/account/prefs') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (array $prefs, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->action(function (array $prefs, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user->setAttribute('prefs', $prefs); @@ -2104,7 +2102,7 @@ App::patch('/v1/account/status') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user->setAttribute('status', false); @@ -2374,7 +2372,7 @@ App::post('/v1/account/recovery') ->inject('locale') ->inject('queueForMails') ->inject('queueForEvents') - ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { + ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index fe96d455fc..786cabc303 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3413,7 +3413,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $processDocument($collection, $document); - $events $queueForEvents ->setParam('databaseId', $databaseId) ->setParam('collectionId', $collection->getId()) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 1557053464..9d598b58b7 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -433,7 +433,7 @@ App::get('/v1/functions/:functionId') App::get('/v1/functions/:functionId/usage') ->desc('Get Function Usage') - ->groups(['api', 'functions']) + ->groups(['api', 'functions', 'usage']) ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'functions') @@ -466,27 +466,24 @@ App::get('/v1/functions/:functionId/usage') str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), ]; - $stats = []; - - Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $periods[$range]['limit']; - $period = $periods[$range]['period']; - - $requestDocs = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric] = []; + foreach ($results as $result) { + $stats[$metric][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; + } + } + }); $format = match ($days['period']) { '1h' => 'Y-m-d\TH:00:00.000P', @@ -1686,7 +1683,6 @@ App::post('/v1/functions/:functionId/executions') ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function ; - } catch (\Throwable $th) { $durationEnd = \microtime(true); @@ -1704,7 +1700,7 @@ App::post('/v1/functions/:functionId/executions') } // TODO revise this later using route label - $usage + $queueForUsage ->setParam('functionId', $function->getId()) ->setParam('executions.{scope}.compute', 1) ->setParam('executionStatus', $execution->getAttribute('status', '')) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 7f3c31a856..ec7f82a29e 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -42,7 +42,6 @@ use Appwrite\Auth\Validator\PersonalData; /** TODO: Remove function when we move to using utopia/platform */ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $queueForEvents): Document -function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Database $dbForProject, Event $queueForEvents): Document { $hashOptionsObject = (\is_string($hashOptions)) ? \json_decode($hashOptions, true) : $hashOptions; // Cast to JSON array $passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0; @@ -191,7 +190,7 @@ App::post('/v1/users/md5') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { $user = createUser('md5', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $queueForEvents); $response @@ -221,7 +220,7 @@ App::post('/v1/users/argon2') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { $user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $queueForEvents); $response @@ -252,7 +251,7 @@ App::post('/v1/users/sha') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Document $project Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { $options = '{}'; if (!empty($passwordVersion)) { diff --git a/composer.json b/composer.json index aa5571ac2e..b5d2392406 100644 --- a/composer.json +++ b/composer.json @@ -71,7 +71,6 @@ "resque/php-resque": "1.3.6", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", - "influxdb/influxdb-php": "1.15.2", "phpmailer/phpmailer": "6.8.0", "chillerlan/php-qrcode": "4.3.4", "adhocore/jwt": "1.1.2", diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 44edb1f0fe..0000000000 --- a/composer.lock +++ /dev/null @@ -1,5222 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "2cf09045bf1484d009c4af490ea4adba", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/61d00f24f9e7766fbba233e7b8d09c5475388073", - "reference": "61d00f24f9e7766fbba233e7b8d09c5475388073", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/1.1.0" - }, - "time": "2020-10-02T05:23:46+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.11.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "9d74a477ba3333cbcfac565c46fcf19606b7b603" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.6.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2022-11-07T16:45:52+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.3", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3", - "reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2021-11-25T22:38:09+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.1", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-01-18T15:43:28+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/1e0104b46f045868f11942aea058cd7186d6c303", - "reference": "1e0104b46f045868f11942aea058cd7186d6c303", - "shasum": "" - }, - "require": { - "composer/package-versions-deprecated": "^1.8.0", - "php": "^7.0|^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0|^8.5|^9.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A wrapper for ocramius/package-versions to get pretty versions strings", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/1.6.0" - }, - "time": "2021-02-04T16:20:16+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9", - "reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2022-04-11T09:58:17+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/953dbc19443aa9314c44b7217a16873347e6840d", - "reference": "953dbc19443aa9314c44b7217a16873347e6840d", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.8.1", - "jean85/pretty-package-versions": "^1.2", - "php": "^7.0 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "squizlabs/php_codesniffer": "^3.5, <3.5.5", - "symfony/phpunit-bridge": "5.x-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.8.0" - }, - "time": "2020-11-25T12:26:02+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.6.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "reference": "e43bac82edc26ca04b36143a48bde1c051cfd5b1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.2", - "php-parallel-lint/php-console-highlighter": "^0.5.0", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.6.2", - "yoast/phpunit-polyfills": "^1.0.0" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2022-02-28T15:31:21+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.18.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "reference": "8496401234f73a49f8c4259d3e89ab4a7c1f9ecf", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.4", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.18.0" - }, - "time": "2023-02-14T09:56:04+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.20.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "reference": "3fce3f4ad3ea9dfcb39b79668abd76331412a5ed", - "shasum": "" - }, - "require": { - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.30.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.20.0" - }, - "time": "2023-02-14T09:46:54+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.30.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "reference": "1cea72c1217357bf0747ae4f28ebef57e9dc0e65", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.0.2" - }, - "require-dev": { - "ext-mongodb": "*", - "ext-redis": "*", - "fakerphp/faker": "^1.14", - "mongodb/mongodb": "1.8.0", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistency using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.30.1" - }, - "time": "2023-02-14T06:25:03+00:00" - }, - { - "name": "utopia-php/domains", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/master" - }, - "time": "2020-02-23T07:40:02+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.26.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", - "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.5.25", - "vimeo/psalm": "4.27.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.26.3" - }, - "time": "2023-06-03T14:01:15+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" - }, - "time": "2023-02-07T05:42:46+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.0.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.8.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.0.2" - }, - "time": "2022-11-08T11:58:46+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "dev-integrate-workers", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "9a3fe2451a815353faf8d12f620b688b39e1f13f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/9a3fe2451a815353faf8d12f620b688b39e1f13f", - "reference": "9a3fe2451a815353faf8d12f620b688b39e1f13f", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.26.*", - "utopia-php/queue": "0.5.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/integrate-workers" - }, - "time": "2023-07-09T15:14:30+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "dev-feat-get-worker-start", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", - "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" - }, - "time": "2023-07-12T07:55:31+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.14.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" - }, - "time": "2023-03-15T00:16:34+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.6.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "289c4327713deadc9c748b5317d248133a02f245" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/289c4327713deadc9c748b5317d248133a02f245", - "reference": "289c4327713deadc9c748b5317d248133a02f245", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.6.0" - }, - "time": "2022-11-07T13:51:59+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.10", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-07-05T14:23:37+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.29.4", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/35ec927d1de1854bebe8894e16b1646c3fdd5567", - "reference": "35ec927d1de1854bebe8894e16b1646c3fdd5567", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.29.4" - }, - "time": "2023-02-03T05:44:59+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" - }, - "time": "2023-06-03T09:27:29+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.71", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2023-04-25T20:33:03+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.16.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" - }, - "time": "2023-06-25T14:52:30+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" - }, - "time": "2023-05-30T18:13:47+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.22.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" - }, - "time": "2023-06-29T20:46:06+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.26", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-03-06T12:58:08+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-02-14T08:28:10+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "4.8.9", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "reference": "8f82ba3b6af04a5bccb97c1654af992d1ee8b0fe", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/4.8.9" - }, - "funding": [ - { - "url": "https://gitee.com/swoole/swoole?donate=true", - "type": "custom" - }, - { - "url": "https://github.com/swoole", - "type": "github" - } - ], - "time": "2022-04-18T20:38:04+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-06-08T12:52:13+00:00" - } - ], - "aliases": [ - { - "package": "utopia-php/platform", - "version": "dev-integrate-workers", - "alias": "0.3.3", - "alias_normalized": "0.3.3.0" - }, - { - "package": "utopia-php/queue", - "version": "dev-feat-get-worker-start", - "alias": "0.5.3", - "alias_normalized": "0.5.3.0" - } - ], - "minimum-stability": "stable", - "stability-flags": { - "utopia-php/queue": 20, - "utopia-php/platform": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.2.0" -} diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index bc8d1bbc72..6645948e32 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -15,7 +15,6 @@ use Appwrite\Platform\Tasks\SSL; use Appwrite\Platform\Tasks\Hamster; use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute; use Appwrite\Platform\Tasks\ClearCardCache; -use Appwrite\Platform\Tasks\Usage; use Appwrite\Platform\Tasks\Vars; use Appwrite\Platform\Tasks\Version; use Appwrite\Platform\Tasks\VolumeSync; @@ -31,7 +30,6 @@ class Tasks extends Service $this->type = self::TYPE_CLI; $this ->addAction(Version::getName(), new Version()) - ->addAction(Usage::getName(), new Usage()) ->addAction(Vars::getName(), new Vars()) ->addAction(SSL::getName(), new SSL()) ->addAction(Hamster::getName(), new Hamster()) diff --git a/src/Appwrite/Platform/Tasks/Usage.php b/src/Appwrite/Platform/Tasks/Usage.php deleted file mode 100644 index fa677ea142..0000000000 --- a/src/Appwrite/Platform/Tasks/Usage.php +++ /dev/null @@ -1,60 +0,0 @@ -desc('Schedules syncing data from influxdb to Appwrite console db') - ->inject('dbForConsole') - ->inject('influxdb') - ->inject('register') - ->inject('getProjectDB') - ->inject('logError') - ->callback(fn ($dbForConsole, $influxDB, $register, $getProjectDB, $logError) => $this->action($dbForConsole, $influxDB, $register, $getProjectDB, $logError)); - } - - protected function aggregateTimeseries(UtopiaDatabase $database, InfluxDatabase $influxDB, callable $logError): void - { - } - - public function action(UtopiaDatabase $dbForConsole, InfluxDatabase $influxDB, Registry $register, callable $getProjectDB, callable $logError) - { - Console::title('Usage Aggregation V1'); - Console::success(APP_NAME . ' usage aggregation process v1 has started'); - - $errorLogger = fn(Throwable $error, string $action = 'syncUsageStats') => $logError($error, "usage", $action); - - $interval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '30'); // 30 seconds (by default) - $region = App::getEnv('region', 'default'); - $usage = new TimeSeries($region, $dbForConsole, $influxDB, $getProjectDB, $register, $errorLogger); - - Console::loop(function () use ($interval, $usage) { - $now = date('d-m-Y H:i:s', time()); - Console::info("[{$now}] Aggregating Timeseries Usage data every {$interval} seconds"); - $loopStart = microtime(true); - - $usage->collect(); - - $loopTook = microtime(true) - $loopStart; - $now = date('d-m-Y H:i:s', time()); - Console::info("[{$now}] Aggregation took {$loopTook} seconds"); - }, $interval); - } -} diff --git a/src/Appwrite/Usage/Calculator.php b/src/Appwrite/Usage/Calculator.php deleted file mode 100644 index 37c130a34a..0000000000 --- a/src/Appwrite/Usage/Calculator.php +++ /dev/null @@ -1,15 +0,0 @@ -region = $region; - } - - abstract public function collect(): void; -} diff --git a/src/Appwrite/Usage/Calculators/TimeSeries.php b/src/Appwrite/Usage/Calculators/TimeSeries.php deleted file mode 100644 index e0a12b443f..0000000000 --- a/src/Appwrite/Usage/Calculators/TimeSeries.php +++ /dev/null @@ -1,557 +0,0 @@ - '1h', - 'startTime' => '-24 hours' - ], - [ - 'key' => '1d', - 'startTime' => '-30 days' - ] - ]; - - /** - * All the metrics that we are collecting - * - * @var array - */ - protected array $metrics = [ - 'project.$all.network.requests' => [ - 'table' => 'appwrite_usage_project_{scope}_network_requests', - ], - 'project.$all.network.bandwidth' => [ - 'table' => 'appwrite_usage_project_{scope}_network_bandwidth', - ], - 'project.$all.network.inbound' => [ - 'table' => 'appwrite_usage_project_{scope}_network_inbound', - ], - 'project.$all.network.outbound' => [ - 'table' => 'appwrite_usage_project_{scope}_network_outbound', - ], - /* Users service metrics */ - 'users.$all.requests.create' => [ - 'table' => 'appwrite_usage_users_{scope}_requests_create', - ], - 'users.$all.requests.read' => [ - 'table' => 'appwrite_usage_users_{scope}_requests_read', - ], - 'users.$all.requests.update' => [ - 'table' => 'appwrite_usage_users_{scope}_requests_update', - ], - 'users.$all.requests.delete' => [ - 'table' => 'appwrite_usage_users_{scope}_requests_delete', - ], - - 'databases.$all.requests.create' => [ - 'table' => 'appwrite_usage_databases_{scope}_requests_create', - ], - 'databases.$all.requests.read' => [ - 'table' => 'appwrite_usage_databases_{scope}_requests_read', - ], - 'databases.$all.requests.update' => [ - 'table' => 'appwrite_usage_databases_{scope}_requests_update', - ], - 'databases.$all.requests.delete' => [ - 'table' => 'appwrite_usage_databases_{scope}_requests_delete', - ], - - 'collections.$all.requests.create' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_create', - ], - 'collections.$all.requests.read' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_read', - ], - 'collections.$all.requests.update' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_update', - ], - 'collections.$all.requests.delete' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_delete', - ], - - 'documents.$all.requests.create' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_create', - ], - 'documents.$all.requests.read' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_read', - ], - 'documents.$all.requests.update' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_update', - ], - 'documents.$all.requests.delete' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_delete', - ], - - 'collections.databaseId.requests.create' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_create', - 'groupBy' => ['databaseId'], - ], - 'collections.databaseId.requests.read' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_read', - 'groupBy' => ['databaseId'], - ], - 'collections.databaseId.requests.update' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_update', - 'groupBy' => ['databaseId'], - ], - 'collections.databaseId.requests.delete' => [ - 'table' => 'appwrite_usage_collections_{scope}_requests_delete', - 'groupBy' => ['databaseId'], - ], - - 'documents.databaseId.requests.create' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_create', - 'groupBy' => ['databaseId'], - ], - 'documents.databaseId.requests.read' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_read', - 'groupBy' => ['databaseId'], - ], - 'documents.databaseId.requests.update' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_update', - 'groupBy' => ['databaseId'], - ], - 'documents.databaseId.requests.delete' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_delete', - 'groupBy' => ['databaseId'], - ], - - 'documents.databaseId/collectionId.requests.create' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_create', - 'groupBy' => ['databaseId', 'collectionId'], - ], - 'documents.databaseId/collectionId.requests.read' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_read', - 'groupBy' => ['databaseId', 'collectionId'], - ], - 'documents.databaseId/collectionId.requests.update' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_update', - 'groupBy' => ['databaseId', 'collectionId'], - ], - 'documents.databaseId/collectionId.requests.delete' => [ - 'table' => 'appwrite_usage_documents_{scope}_requests_delete', - 'groupBy' => ['databaseId', 'collectionId'], - ], - - 'buckets.$all.requests.create' => [ - 'table' => 'appwrite_usage_buckets_{scope}_requests_create', - ], - 'buckets.$all.requests.read' => [ - 'table' => 'appwrite_usage_buckets_{scope}_requests_read', - ], - 'buckets.$all.requests.update' => [ - 'table' => 'appwrite_usage_buckets_{scope}_requests_update', - ], - 'buckets.$all.requests.delete' => [ - 'table' => 'appwrite_usage_buckets_{scope}_requests_delete', - ], - - 'files.$all.requests.create' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_create', - ], - 'files.$all.requests.read' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_read', - ], - 'files.$all.requests.update' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_update', - ], - 'files.$all.requests.delete' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_delete', - ], - - 'files.bucketId.requests.create' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_create', - 'groupBy' => ['bucketId'], - ], - 'files.bucketId.requests.read' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_read', - 'groupBy' => ['bucketId'], - ], - 'files.bucketId.requests.update' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_update', - 'groupBy' => ['bucketId'], - ], - 'files.bucketId.requests.delete' => [ - 'table' => 'appwrite_usage_files_{scope}_requests_delete', - 'groupBy' => ['bucketId'], - ], - - 'sessions.$all.requests.create' => [ - 'table' => 'appwrite_usage_sessions__{scope}_requests_create', - ], - 'sessions.provider.requests.create' => [ - 'table' => 'appwrite_usage_sessions_{scope}_requests_create', - 'groupBy' => ['provider'], - ], - 'sessions.$all.requests.delete' => [ - 'table' => 'appwrite_usage_sessions_{scope}_requests_delete', - ], - 'executions.$all.compute.total' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute', - ], - 'builds.$all.compute.total' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute', - ], - 'executions.$all.compute.failure' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute', - 'filters' => [ - 'functionStatus' => 'failed', - ], - ], - 'builds.$all.compute.failure' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute', - 'filters' => [ - 'functionStatus' => 'failed', - ], - ], - 'executions.$all.compute.success' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute', - 'filters' => [ - 'functionStatus' => 'success', - ], - ], - 'builds.$all.compute.success' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute', - 'filters' => [ - 'functionStatus' => 'success', - ], - ], - 'executions.functionId.compute.total' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute', - 'groupBy' => ['functionId'], - ], - 'builds.functionId.compute.total' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute', - 'groupBy' => ['functionId'], - ], - - 'executions.functionId.compute.failure' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute', - 'groupBy' => ['functionId'], - 'filters' => [ - 'functionStatus' => 'failed', - ], - ], - 'builds.functionId.compute.failure' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute', - 'groupBy' => ['functionId'], - 'filters' => [ - 'functionBuildStatus' => 'failed', - ], - ], - 'executions.functionId.compute.success' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute', - 'groupBy' => ['functionId'], - 'filters' => [ - 'functionStatus' => 'success', - ], - ], - 'builds.functionId.compute.success' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute', - 'groupBy' => ['functionId'], - 'filters' => [ - 'functionBuildStatus' => 'success', - ], - ], - - // counters - 'users.$all.count.total' => [ - 'table' => 'appwrite_usage_users_{scope}_count_total', - ], - 'buckets.$all.count.total' => [ - 'table' => 'appwrite_usage_buckets_{scope}_count_total', - ], - 'files.$all.count.total' => [ - 'table' => 'appwrite_usage_files_{scope}_count_total', - ], - 'files.bucketId.count.total' => [ - 'table' => 'appwrite_usage_files_{scope}_count_total', - 'groupBy' => ['bucketId'] - ], - 'databases.$all.count.total' => [ - 'table' => 'appwrite_usage_databases_{scope}_count_total', - ], - 'collections.$all.count.total' => [ - 'table' => 'appwrite_usage_collections_{scope}_count_total', - ], - 'documents.$all.count.total' => [ - 'table' => 'appwrite_usage_documents_{scope}_count_total', - ], - 'collections.databaseId.count.total' => [ - 'table' => 'appwrite_usage_collections_{scope}_count_total', - 'groupBy' => ['databaseId'] - ], - 'documents.databaseId.count.total' => [ - 'table' => 'appwrite_usage_documents_{scope}_count_total', - 'groupBy' => ['databaseId'] - ], - 'documents.databaseId/collectionId.count.total' => [ - 'table' => 'appwrite_usage_documents_{scope}_count_total', - 'groupBy' => ['databaseId', 'collectionId'] - ], - 'deployments.$all.storage.size' => [ - 'table' => 'appwrite_usage_deployments_{scope}_storage_size', - ], - 'project.$all.storage.size' => [ - 'table' => 'appwrite_usage_project_{scope}_storage_size', - ], - 'files.$all.storage.size' => [ - 'table' => 'appwrite_usage_files_{scope}_storage_size', - ], - 'files.$bucketId.storage.size' => [ - 'table' => 'appwrite_usage_files_{scope}_storage_size', - 'groupBy' => ['bucketId'] - ], - - 'builds.$all.compute.time' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute_time', - ], - 'executions.$all.compute.time' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute_time', - ], - - 'executions.functionId.compute.time' => [ - 'table' => 'appwrite_usage_executions_{scope}_compute_time', - 'groupBy' => ['functionId'], - ], - 'builds.functionId.compute.time' => [ - 'table' => 'appwrite_usage_builds_{scope}_compute_time', - 'groupBy' => ['functionId'], - ], - - 'project.$all.compute.time' => [ // Built time + execution time - 'table' => 'appwrite_usage_project_{scope}_compute_time', - 'groupBy' => ['functionId'], - ], - - 'deployments.$all.storage.size' => [ - 'table' => 'appwrite_usage_deployments_{scope}_storage_size' - ], - 'project.$all.storage.size' => [ - 'table' => 'appwrite_usage_project_{scope}_storage_size' - ], - 'files.$all.storage.size' => [ - 'table' => 'appwrite_usage_files_{scope}_storage_size' - ], - 'files.bucketId.storage.size' => [ - 'table' => 'appwrite_usage_files_{scope}_storage_size', - 'groupBy' => ['bucketId'] - ] - ]; - - public function __construct(string $region, Database $database, InfluxDatabase $influxDB, callable $getProjectDB, Registry $register, callable $errorHandler = null) - { - parent::__construct($region); - $this->database = $database; - $this->influxDB = $influxDB; - $this->getProjectDB = $getProjectDB; - $this->register = $register; - $this->errorHandler = $errorHandler; - } - - /** - * Create or Update Mertic - * Create or update each metric in the stats collection for the given project - * - * @param string $projectId - * @param int $time - * @param string $period - * @param string $metric - * @param int $value - * @param int $type - * - * @return void - */ - private function createOrUpdateMetric(string $projectId, string $time, string $period, string $metric, int $value, int $type): void - { - $id = \md5("{$time}_{$period}_{$metric}"); - $project = $this->database->getDocument('projects', $projectId); - $database = call_user_func($this->getProjectDB, $project); - - try { - $document = $database->getDocument('stats', $id); - if ($document->isEmpty()) { - $database->createDocument('stats', new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $metric, - 'value' => $value, - 'type' => $type, - 'region' => $this->region, - ])); - } else { - $database->updateDocument( - 'stats', - $document->getId(), - $document->setAttribute('value', $value) - ); - } - } catch (\Exception $e) { // if projects are deleted this might fail - if (is_callable($this->errorHandler)) { - call_user_func($this->errorHandler, $e, "sync_project_{$projectId}_metric_{$metric}"); - } else { - throw $e; - } - } - - $this->register->get('pools')->reclaim(); - } - - /** - * Sync From InfluxDB - * Sync stats from influxDB to stats collection in the Appwrite database - * - * @param string $metric - * @param array $options - * @param array $period - * - * @return void - */ - private function syncFromInfluxDB(string $metric, array $options, array $period): void - { - $start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339); - if (!empty($this->latestTime[$metric][$period['key']])) { - $start = $this->latestTime[$metric][$period['key']]; - } - $end = (new DateTime())->format(DateTime::RFC3339); - - $table = $options['table']; //Which influxdb table to query for this metric - $groupBy = empty($options['groupBy']) ? '' : ', ' . implode(', ', array_map(fn($groupBy) => '"' . $groupBy . '" ', $options['groupBy'])); //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc - - $filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status - if (!empty($filters)) { - $filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters))); - } else { - $filters = ''; - } - - $query = "SELECT sum(value) AS \"value\" "; - $query .= "FROM \"{$table}\" "; - $query .= "WHERE \"time\" > '{$start}' "; - $query .= "AND \"time\" < '{$end}' "; - $query .= "AND \"metric_type\"='counter' {$filters} "; - $query .= "GROUP BY time({$period['key']}), \"projectId\" {$groupBy} "; - $query .= "FILL(null)"; - - try { - $result = $this->influxDB->query($query); - $points = $result->getPoints(); - foreach ($points as $point) { - $projectId = $point['projectId']; - - if (!empty($projectId) && $projectId !== 'console') { - $metricUpdated = $metric; - if (!empty($groupBy)) { - foreach ($options['groupBy'] as $groupBy) { - $groupedBy = $point[$groupBy] ?? ''; - if (empty($groupedBy)) { - continue; - } - $metricUpdated = str_replace($groupBy, $groupedBy, $metricUpdated); - } - } - - $value = (!empty($point['value'])) ? $point['value'] : 0; - - $this->createOrUpdateMetric( - $point['projectId'], - $point['time'], - $period['key'], - $metricUpdated, - $value, - 0 - ); - $this->latestTime[$metric][$period['key']] = $point['time']; - } - } - } catch (\Exception $e) { // if projects are deleted this might fail - if (is_callable($this->errorHandler)) { - call_user_func($this->errorHandler, $e, "sync_metric_{$metric}_influxdb"); - } else { - throw $e; - } - } - } - - /** - * Collect Stats - * Collect all the stats from Influd DB to Database - * - * @return void - */ - public function collect(): void - { - foreach ($this->periods as $period) { - foreach ($this->metrics as $metric => $options) { //for each metrics - try { - $this->syncFromInfluxDB($metric, $options, $period); - } catch (\Exception $e) { - if (is_callable($this->errorHandler)) { - call_user_func($this->errorHandler, $e); - } else { - throw $e; - } - } - } - } - } -} diff --git a/src/Appwrite/Usage/Stats.php b/src/Appwrite/Usage/Stats.php deleted file mode 100644 index e6e0056664..0000000000 --- a/src/Appwrite/Usage/Stats.php +++ /dev/null @@ -1,225 +0,0 @@ -statsd = $statsd; - } - - /** - * @param string $key - * @param mixed $value - * - * @return $this - */ - public function setParam(string $key, $value): self - { - $this->params[$key] = $value; - - return $this; - } - - /** - * @param string $key - * - * @return mixed|null - */ - public function getParam(string $key) - { - return (isset($this->params[$key])) ? $this->params[$key] : null; - } - - /** - * @param string $namespace - * - * @return $this - */ - public function setNamespace(string $namespace): self - { - $this->namespace = $namespace; - - return $this; - } - - /** - * @return string - */ - public function getNamespace() - { - return $this->namespace; - } - - /** - * Submit data to StatsD. - * Send various metrics to StatsD based on the parameters that are set - * @return void - */ - public function submit(): void - { - $projectId = $this->params['projectId'] ?? ''; - $projectInternalId = $this->params['projectInternalId']; - $tags = ",projectInternalId={$projectInternalId},projectId={$projectId},version=" . App::getEnv('_APP_VERSION', 'UNKNOWN'); - - // the global namespace is prepended to every key (optional) - $this->statsd->setNamespace($this->namespace); - - $httpRequest = $this->params['project.{scope}.network.requests'] ?? 0; - $httpMethod = $this->params['httpMethod'] ?? ''; - if ($httpRequest >= 1) { - $this->statsd->increment('project.{scope}.network.requests' . $tags . ',method=' . \strtolower($httpMethod)); - } - - $inbound = $this->params['project.{scope}.network.inbound'] ?? 0; - $outbound = $this->params['project.{scope}.network.outbound'] ?? 0; - $this->statsd->count('project.{scope}.network.inbound' . $tags, $inbound); - $this->statsd->count('project.{scope}.network.outbound' . $tags, $outbound); - $this->statsd->count('project.{scope}.network.bandwidth' . $tags, $inbound + $outbound); - - $usersMetrics = [ - 'users.{scope}.requests.create', - 'users.{scope}.requests.read', - 'users.{scope}.requests.update', - 'users.{scope}.requests.delete', - 'users.{scope}.count.total', - ]; - - foreach ($usersMetrics as $metric) { - $value = $this->params[$metric] ?? 0; - if ($value === 1 || $value === -1) { - $this->statsd->count($metric . $tags, $value); - } - } - - $dbMetrics = [ - 'databases.{scope}.requests.create', - 'databases.{scope}.requests.read', - 'databases.{scope}.requests.update', - 'databases.{scope}.requests.delete', - 'collections.{scope}.requests.create', - 'collections.{scope}.requests.read', - 'collections.{scope}.requests.update', - 'collections.{scope}.requests.delete', - 'documents.{scope}.requests.create', - 'documents.{scope}.requests.read', - 'documents.{scope}.requests.update', - 'documents.{scope}.requests.delete', - 'databases.{scope}.count.total', - 'collections.{scope}.count.total', - 'documents.{scope}.count.total' - ]; - - foreach ($dbMetrics as $metric) { - $value = $this->params[$metric] ?? 0; - if ($value === 1 || $value === -1) { - $dbTags = $tags . ",collectionId=" . ($this->params['collectionId'] ?? '') . ",databaseId=" . ($this->params['databaseId'] ?? ''); - $this->statsd->count($metric . $dbTags, $value); - } - } - - $storageMertics = [ - 'buckets.{scope}.requests.create', - 'buckets.{scope}.requests.read', - 'buckets.{scope}.requests.update', - 'buckets.{scope}.requests.delete', - 'files.{scope}.requests.create', - 'files.{scope}.requests.read', - 'files.{scope}.requests.update', - 'files.{scope}.requests.delete', - 'buckets.{scope}.count.total', - 'files.{scope}.count.total', - 'files.{scope}.storage.size' - ]; - - foreach ($storageMertics as $metric) { - $value = $this->params[$metric] ?? 0; - if ($value !== 0) { - $storageTags = $tags . ",bucketId=" . ($this->params['bucketId'] ?? ''); - $this->statsd->count($metric . $storageTags, $value); - } - } - - $sessionsMetrics = [ - 'sessions.{scope}.requests.create', - 'sessions.{scope}.requests.update', - 'sessions.{scope}.requests.delete', - ]; - - foreach ($sessionsMetrics as $metric) { - $value = $this->params[$metric] ?? 0; - if ($value >= 1) { - $sessionTags = $tags . ",provider=" . ($this->params['provider'] ?? ''); - $this->statsd->count($metric . $sessionTags, $value); - } - } - - $functionId = $this->params['functionId'] ?? ''; - $functionExecution = $this->params['executions.{scope}.compute'] ?? 0; - $functionExecutionTime = ($this->params['executionTime'] ?? 0) * 1000; // ms - $functionExecutionStatus = $this->params['executionStatus'] ?? ''; - - $functionBuild = $this->params['builds.{scope}.compute'] ?? 0; - $functionBuildTime = ($this->params['buildTime'] ?? 0) * 1000; // ms - $functionBuildStatus = $this->params['buildStatus'] ?? ''; - $functionCompute = $functionExecutionTime + $functionBuildTime; - $functionTags = $tags . ',functionId=' . $functionId; - - $deploymentSize = $this->params['deployment.{scope}.storage.size'] ?? 0; - $storageSize = $this->params['files.{scope}.storage.size'] ?? 0; - if ($deploymentSize + $storageSize > 0 || $deploymentSize + $storageSize <= -1) { - $this->statsd->count('project.{scope}.storage.size' . $tags, $deploymentSize + $storageSize); - } - - if ($deploymentSize !== 0) { - $this->statsd->count('deployments.{scope}.storage.size' . $functionTags, $deploymentSize); - } - - if ($functionExecution >= 1) { - $this->statsd->increment('executions.{scope}.compute' . $functionTags . ',functionStatus=' . $functionExecutionStatus); - if ($functionExecutionTime > 0) { - $this->statsd->count('executions.{scope}.compute.time' . $functionTags, $functionExecutionTime); - } - } - if ($functionBuild >= 1) { - $this->statsd->increment('builds.{scope}.compute' . $functionTags . ',functionBuildStatus=' . $functionBuildStatus); - $this->statsd->count('builds.{scope}.compute.time' . $functionTags, $functionBuildTime); - } - if ($functionBuild + $functionExecution >= 1) { - $this->statsd->count('project.{scope}.compute.time' . $functionTags, $functionCompute); - } - - $this->reset(); - } - - public function reset(): self - { - $this->params = []; - $this->namespace = 'appwrite.usage'; - - return $this; - } -} diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 3eea7b7b7e..975f24ccf5 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Database\Validator\Queries; -use Appwrite\Extend\Exception; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; From a10246a9bd13cc1b9a08dfa3d375c8a9bc175036 Mon Sep 17 00:00:00 2001 From: shimon Date: Thu, 28 Sep 2023 13:45:15 +0300 Subject: [PATCH 044/144] build && audit && usage workers --- app/worker.php | 7 +- src/Appwrite/Platform/Workers/Audits.php | 6 +- src/Appwrite/Platform/Workers/Builds.php | 749 +++++++++++++++++--- src/Appwrite/Platform/Workers/UsageHook.php | 6 - 4 files changed, 636 insertions(+), 132 deletions(-) diff --git a/app/worker.php b/app/worker.php index 2b1e6bb2d5..ed499eca9b 100644 --- a/app/worker.php +++ b/app/worker.php @@ -218,8 +218,6 @@ if (isset($args[0])) { Console::error('Missing worker name'); } -if (empty(App::getEnv('QUEUE'))) { - throw new Exception('Please configure "QUEUE" environment variable.'); try { $platform->init(Service::TYPE_WORKER, [ 'workersNum' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), @@ -251,9 +249,6 @@ $worker throw $error; } - if (($error->getCode() >= 500 || $error->getCode() === 0) && !empty($logger)) { - $log = new Log(); - if ($logger && ($error->getCode() >= 500 || $error->getCode() === 0)) { $log->setNamespace("appwrite-worker"); $log->setServer(\gethostname()); @@ -291,4 +286,4 @@ try { }); } -$worker->start(); +$worker->start(); \ No newline at end of file diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index f889f5e939..ac6388855e 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Workers; use Exception; -use Utopia\App; +use Throwable; use Utopia\Audit\Audit; use Utopia\Database\Database; use Utopia\Database\Document; @@ -32,6 +32,7 @@ class Audits extends Action /** * @throws Exception + * @throws Throwable */ public function action(Message $message, Database $dbForProject): void { @@ -55,8 +56,7 @@ class Audits extends Action $audit = new Audit($dbForProject); $audit->log( - userInternalId: $user->getInternalId(), - userId: $user->getId(), + userId: $user->getInternalId(), // Pass first, most verbose event pattern event: $event, resource: $resource, diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 7dbdc9b67f..6dbcac637e 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -7,21 +7,26 @@ use Appwrite\Event\Func; use Appwrite\Event\Usage; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Deployment; +use Appwrite\Vcs\Comment; use Exception; +use Swoole\Coroutine as Co; use Executor\Executor; use Utopia\App; +use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\DSN\DSN; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; +use Utopia\VCS\Adapter\Git\GitHub; class Builds extends Action { @@ -43,13 +48,15 @@ class Builds extends Action ->inject('queueForEvents') ->inject('queueForFunctions') ->inject('queueForUsage') - ->callback(fn($message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage) => $this->action($message, $dbForConsole, $dbForProject, $queueForEvents, $queueForFunctions, $queueForUsage)); + ->inject('cache') + ->inject('getProjectDB') + ->callback(fn($message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB) => $this->action($message, $dbForConsole, $dbForProject, $queueForEvents, $queueForFunctions, $queueForUsage, $cache, $getProjectDB)); } /** * @throws Exception|\Throwable */ - public function action(Message $message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage): void + public function action(Message $message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB): void { $payload = $message->getPayload() ?? []; @@ -61,21 +68,14 @@ class Builds extends Action $project = new Document($payload['project'] ?? []); $resource = new Document($payload['resource'] ?? []); $deployment = new Document($payload['deployment'] ?? []); + $template = new Document($payload['template'] ?? []); switch ($type) { case BUILD_TYPE_DEPLOYMENT: case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); - $this->buildDeployment( - dbForConsole: $dbForConsole, - dbForProject: $dbForProject, - queueForEvents: $queueForEvents, - queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, - deployment: $deployment, - project: $project, - function: $resource - ); + $github = new GitHub($cache); + $this->buildDeployment($queueForEvents, $queueForUsage, $getProjectDB, $dbForConsole, $github, $project, $resource, $deployment, $template); break; default: @@ -88,11 +88,13 @@ class Builds extends Action * @throws \Throwable * @throws Structure */ - private function buildDeployment(Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Document $deployment, Document $project, Document $function): void + protected function buildDeployment(Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template) { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); - $function = $dbForProject->getDocument('functions', $function->getId()); + $dbForProject = $getProjectDB($project); + + $function = $dbForProject->getDocument('functions', $function->getId()); if ($function->isEmpty()) { throw new Exception('Function not found', 404); } @@ -102,27 +104,33 @@ class Builds extends Action throw new Exception('Deployment not found', 404); } - $runtimes = Config::getParam('runtimes', []); + if (empty($deployment->getAttribute('entrypoint', ''))) { + throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); + } + + $version = $function->getAttribute('version', 'v2'); + $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); $key = $function->getAttribute('runtime'); $runtime = $runtimes[$key] ?? null; if (\is_null($runtime)) { throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); - /** @TODO : move this to the registry or someplace else */ - $device = Storage::DEVICE_LOCAL; - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - } catch (\Exception $e) { - Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } + + // Realtime preparation + $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ + 'functionId' => $function->getId(), + 'deploymentId' => $deployment->getId() + ]); + + $startTime = DateTime::now(); + $durationStart = \microtime(true); $buildId = $deployment->getAttribute('buildId', ''); - $startTime = DateTime::now(); - if (empty($buildId)) { + $isNewBuild = empty($buildId); + + if ($isNewBuild) { $buildId = ID::unique(); $build = $dbForProject->createDocument('builds', new Document([ '$id' => $buildId, @@ -131,106 +139,323 @@ class Builds extends Action 'deploymentInternalId' => $deployment->getInternalId(), 'deploymentId' => $deployment->getId(), 'status' => 'processing', - 'outputPath' => '', + 'path' => '', 'runtime' => $function->getAttribute('runtime'), - 'source' => $deployment->getAttribute('path'), - 'sourceType' => $device, - 'stdout' => '', - 'stderr' => '', - 'duration' => 0 + 'source' => $deployment->getAttribute('path', ''), + 'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)), + 'logs' => '', + 'endTime' => null, + 'duration' => 0, + 'size' => 0 ])); - $deployment->setAttribute('buildId', $buildId); + $deployment->setAttribute('buildId', $build->getId()); $deployment->setAttribute('buildInternalId', $build->getInternalId()); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); } else { $build = $dbForProject->getDocument('builds', $buildId); } - /** Request the executor to build the code... */ - $build->setAttribute('status', 'building'); - $build = $dbForProject->updateDocument('builds', $buildId, $build); + $source = $deployment->getAttribute('path', ''); + $installationId = $deployment->getAttribute('installationId', ''); + $providerRepositoryId = $deployment->getAttribute('providerRepositoryId', ''); + $providerCommitHash = $deployment->getAttribute('providerCommitHash', ''); + $isVcsEnabled = $providerRepositoryId ? true : false; + $owner = ''; + $repositoryName = ''; - /** Trigger Webhook */ - $deploymentUpdate = $queueForEvents - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setProject($project) - ->setEvent('functions.[functionId].deployments.[deploymentId].update') - ->setParam('functionId', $function->getId()) - ->setParam('deploymentId', $deployment->getId()) - ->setPayload($deployment->getArrayCopy( - array_keys( - (new Deployment())->getRules() - ) - )); + if ($isVcsEnabled) { + $installation = $dbForConsole->getDocument('installations', $installationId); + $providerInstallationId = $installation->getAttribute('providerInstallationId'); + $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); - $deploymentUpdate->trigger(); - - /** Trigger Functions */ - $queueForFunctions - ->from($deploymentUpdate) - ->trigger(); - - - /** Trigger Realtime */ - $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ - 'functionId' => $function->getId(), - 'deploymentId' => $deployment->getId() - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - - $source = $deployment->getAttribute('path'); - - $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { - $carry[$var->getAttribute('key')] = $var->getAttribute('value'); - return $carry; - }, []); + $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); + } try { - $response = $executor->createRuntime( - deploymentId: $deployment->getId(), - projectId: $project->getId(), - source: $source, - image: $runtime['image'], - remove: true, - entrypoint: $deployment->getAttribute('entrypoint'), - workdir: '/usr/code', - destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", - variables: $vars, - commands: [ - 'sh', '-c', - 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ - cd /usr/local/src/ && ./build.sh' - ] + if ($isNewBuild && $isVcsEnabled) { + $tmpDirectory = '/tmp/builds/' . $buildId . '/code'; + $rootDirectory = $function->getAttribute('providerRootDirectory', ''); + $rootDirectory = \rtrim($rootDirectory, '/'); + $rootDirectory = \ltrim($rootDirectory, '.'); + $rootDirectory = \ltrim($rootDirectory, '/'); + + $owner = $github->getOwnerName($providerInstallationId); + $repositoryName = $github->getRepositoryName($providerRepositoryId); + + $cloneOwner = $deployment->getAttribute('providerRepositoryOwner', $owner); + $cloneRepository = $deployment->getAttribute('providerRepositoryName', $repositoryName); + + $branchName = $deployment->getAttribute('providerBranch'); + $commitHash = $deployment->getAttribute('providerCommitHash', ''); + $gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $branchName, $tmpDirectory, $rootDirectory, $commitHash); + $stdout = ''; + $stderr = ''; + Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr); + $exit = Console::execute($gitCloneCommand, '', $stdout, $stderr); + + if ($exit !== 0) { + throw new \Exception('Unable to clone code repository: ' . $stderr); + } + + // Build from template + $templateRepositoryName = $template->getAttribute('repositoryName', ''); + $templateOwnerName = $template->getAttribute('ownerName', ''); + $templateBranch = $template->getAttribute('branch', ''); + + $templateRootDirectory = $template->getAttribute('rootDirectory', ''); + $templateRootDirectory = \rtrim($templateRootDirectory, '/'); + $templateRootDirectory = \ltrim($templateRootDirectory, '.'); + $templateRootDirectory = \ltrim($templateRootDirectory, '/'); + + if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) { + // Clone template repo + $tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '/template'; + $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $tmpTemplateDirectory, $templateRootDirectory); + $exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr); + + if ($exit !== 0) { + throw new \Exception('Unable to clone code repository: ' . $stderr); + } + + // Ensure directories + Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr); + Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr); + + // Merge template into user repo + Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr); + + // Commit and push + $exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $stdout, $stderr); + + if ($exit !== 0) { + throw new \Exception('Unable to push code repository: ' . $stderr); + } + + $exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $stdout, $stderr); + + if ($exit !== 0) { + throw new \Exception('Unable to get vcs commit SHA: ' . $stderr); + } + + $providerCommitHash = \trim($stdout); + $authorUrl = "https://github.com/$cloneOwner"; + + $deployment->setAttribute('providerCommitHash', $providerCommitHash ?? ''); + $deployment->setAttribute('providerCommitAuthorUrl', $authorUrl); + $deployment->setAttribute('providerCommitAuthor', 'Appwrite'); + $deployment->setAttribute('providerCommitMessage', "Create '" . $function->getAttribute('name', '') . "' function"); + $deployment->setAttribute('providerCommitUrl', "https://github.com/$cloneOwner/$cloneRepository/commit/$providerCommitHash"); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + + /** + * Send realtime Event + */ + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project + ); + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + } + + $tmpPath = '/tmp/builds/' . \escapeshellcmd($buildId); + $tmpPathFile = $tmpPath . '/code.tar.gz'; + + Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); + + $deviceFunctions = $this->getFunctionsDevice($project->getId()); + + $localDevice = new Local(); + $buffer = $localDevice->read($tmpPathFile); + $mimeType = $localDevice->getFileMimeType($tmpPathFile); + + $path = $deviceFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); + $result = $deviceFunctions->write($path, $buffer, $mimeType); + + if (!$result) { + throw new \Exception("Unable to move file"); + } + + Console::execute('rm -rf ' . $tmpPath, '', $stdout, $stderr); + + $source = $path; + + $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source)); + + $this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); + } + + /** Request the executor to build the code... */ + $build->setAttribute('status', 'building'); + $build = $dbForProject->updateDocument('builds', $buildId, $build); + + if ($isVcsEnabled) { + $this->runGitAction('building', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); + } + + /** Trigger Webhook */ + $deploymentUpdate = + $queueForEvents + ->setQueue(Event::WEBHOOK_QUEUE_NAME) + ->setClass(Event::WEBHOOK_CLASS_NAME) + ->setProject($project) + ->setEvent('functions.[functionId].deployments.[deploymentId].update') + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($deployment->getArrayCopy( + array_keys( + (new Deployment())->getRules() + ) + )); + + $deploymentUpdate->trigger(); + + /** Trigger Functions */ + $queueForFunctions + ->from($deploymentUpdate) + ->trigger(); + + /** Trigger Realtime */ + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project ); - $endTime = new \DateTime(); - $endTime->setTimestamp($response['endTimeUnix']); + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + + $vars = []; + + // Shared vars + foreach ($function->getAttribute('varsProject', []) as $var) { + $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); + } + + // Function vars + foreach ($function->getAttribute('vars', []) as $var) { + $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); + } + + // Appwrite vars + $vars = \array_merge($vars, [ + 'APPWRITE_FUNCTION_ID' => $function->getId(), + 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), + 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), + 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', + ]); + + $command = $deployment->getAttribute('commands', ''); + $command = \str_replace('"', '\\"', $command); + + $response = null; + + $err = null; + + // TODO: Remove run() wrapper when switching to new utopia queue. That should be done on Swoole adapter in the libary + Co\run(function () use ($executor, $project, $deployment, &$response, $source, $function, $runtime, $vars, $command, &$build, $dbForProject, $allEvents, &$err) { + Co::join([ + Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) { + try { + $version = $function->getAttribute('version', 'v2'); + $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'; + + $response = $executor->createRuntime( + deploymentId: $deployment->getId(), + projectId: $project->getId(), + source: $source, + image: $runtime['image'], + version: $version, + remove: true, + entrypoint: $deployment->getAttribute('entrypoint'), + destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", + variables: $vars, + command: $command + ); + } catch (Exception $error) { + $err = $error; + } + }), + Co\go(function () use ($executor, $project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err) { + try { + $executor->getLogs( + deploymentId: $deployment->getId(), + projectId: $project->getId(), + callback: function ($logs) use (&$response, &$build, $dbForProject, $allEvents, $project) { + if ($response === null) { + $build = $dbForProject->getDocument('builds', $build->getId()); + + if ($build->isEmpty()) { + throw new Exception('Build not found', 404); + } + + $build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs); + $build = $dbForProject->updateDocument('builds', $build->getId(), $build); + + /** + * Send realtime Event + */ + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $build, + project: $project + ); + Realtime::send( + projectId: 'console', + payload: $build->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + } + } + ); + } catch (Exception $error) { + if (empty($err)) { + $err = $error; + } + } + }), + ]); + }); + + if ($err) { + throw $err; + } + + $endTime = DateTime::now(); + $durationEnd = \microtime(true); /** Update the build document */ - $build->setAttribute('endTime', DateTime::format($endTime)); - $build->setAttribute('duration', \intval($response['duration'])); - $build->setAttribute('status', $response['status']); - $build->setAttribute('outputPath', $response['outputPath']); - $build->setAttribute('stderr', $response['stderr']); - $build->setAttribute('stdout', $response['stdout']); + $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); + $build->setAttribute('endTime', $endTime); + $build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart))); + $build->setAttribute('status', 'ready'); + $build->setAttribute('path', $response['path']); + $build->setAttribute('size', $response['size']); + $build->setAttribute('logs', $response['output']); - /* Also update the deployment buildTime */ - $deployment->setAttribute('buildTime', $response['duration']); + if ($isVcsEnabled) { + $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); + } Console::success("Build id: $buildId created"); @@ -238,27 +463,33 @@ class Builds extends Action if ($deployment->getAttribute('activate') === true) { $function->setAttribute('deploymentInternalId', $deployment->getInternalId()); $function->setAttribute('deployment', $deployment->getId()); + $function->setAttribute('live', true); $function = $dbForProject->updateDocument('functions', $function->getId(), $function); } /** Update function schedule */ - $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule->setAttribute('resourceUpdatedAt', DateTime::now()); + // Inform scheduler if function is still active + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - - - \Utopia\Database\Validator\Authorization::skip(fn() => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn() => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); } catch (\Throwable $th) { $endTime = DateTime::now(); - $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); + $durationEnd = \microtime(true); $build->setAttribute('endTime', $endTime); - $build->setAttribute('duration', $interval->format('%s') + 0); + $build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart))); $build->setAttribute('status', 'failed'); - $build->setAttribute('stderr', $th->getMessage()); + $build->setAttribute('logs', $th->getMessage()); Console::error($th->getMessage()); + Console::error($th->getFile() . ':' . $th->getLine()); + Console::error($th->getTraceAsString()); + + if ($isVcsEnabled) { + $this->runGitAction('failed', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); + } } finally { $build = $dbForProject->updateDocument('builds', $buildId, $build); @@ -291,4 +522,288 @@ class Builds extends Action ->trigger(); } } + + protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole): void + { + if ($function->getAttribute('providerSilentMode', false) === true) { + return; + } + + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + $commentId = $deployment->getAttribute('providerCommentId', ''); + + if (!empty($providerCommitHash)) { + $message = match ($status) { + 'ready' => 'Build succeeded.', + 'failed' => 'Build failed.', + 'processing' => 'Building...', + default => $status + }; + + $state = match ($status) { + 'ready' => 'success', + 'failed' => 'failure', + 'processing' => 'pending', + default => $status + }; + + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + + $name = "{$functionName} ({$projectName})"; + + $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; + $hostname = App::getEnv('_APP_DOMAIN'); + $functionId = $function->getId(); + $projectId = $project->getId(); + $providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$projectId/functions/function-$functionId"; + + $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, $state, $message, $providerTargetUrl, $name); + } + + if (!empty($commentId)) { + $retries = 0; + + while (true) { + $retries++; + + try { + $dbForConsole->createDocument('vcsCommentLocks', new Document([ + '$id' => $commentId + ])); + break; + } catch (Exception $err) { + if ($retries >= 9) { + throw $err; + } + + \sleep(1); + } + } + + // Wrap in try/finally to ensure lock file gets deleted + try { + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); + $comment->addBuild($project, $function, $status, $deployment->getId(), ['type' => 'logs']); + $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); + } finally { + $dbForConsole->deleteDocument('vcsCommentLocks', $commentId); + } + } + } } + +// // Realtime preparation +// $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ +// 'functionId' => $function->getId(), +// 'deploymentId' => $deployment->getId() +// ]); +// +// $startTime = DateTime::now(); +// $durationStart = \microtime(true); +// +// +// +// $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); +// $function = $dbForProject->getDocument('functions', $function->getId()); +// +// if ($function->isEmpty()) { +// throw new Exception('Function not found', 404); +// } +// +// $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); +// if ($deployment->isEmpty()) { +// throw new Exception('Deployment not found', 404); +// } +// +// $runtimes = Config::getParam('runtimes', []); +// $key = $function->getAttribute('runtime'); +// $runtime = $runtimes[$key] ?? null; +// if (\is_null($runtime)) { +// throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); +// } +// +// $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); +// /** @TODO : move this to the registry or someplace else */ +// $device = Storage::DEVICE_LOCAL; +// try { +// $dsn = new DSN($connection); +// $device = $dsn->getScheme(); +// } catch (\Exception $e) { +// Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); +// } +// +// $buildId = $deployment->getAttribute('buildId', ''); +// $startTime = DateTime::now(); +// +// if (empty($buildId)) { +// $buildId = ID::unique(); +// $build = $dbForProject->createDocument('builds', new Document([ +// '$id' => $buildId, +// '$permissions' => [], +// 'startTime' => $startTime, +// 'deploymentInternalId' => $deployment->getInternalId(), +// 'deploymentId' => $deployment->getId(), +// 'status' => 'processing', +// 'outputPath' => '', +// 'runtime' => $function->getAttribute('runtime'), +// 'source' => $deployment->getAttribute('path'), +// 'sourceType' => $device, +// 'stdout' => '', +// 'stderr' => '', +// 'duration' => 0 +// ])); +// +// $deployment->setAttribute('buildId', $buildId); +// $deployment->setAttribute('buildInternalId', $build->getInternalId()); +// $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); +// } else { +// $build = $dbForProject->getDocument('builds', $buildId); +// } +// +// /** Request the executor to build the code... */ +// $build->setAttribute('status', 'building'); +// $build = $dbForProject->updateDocument('builds', $buildId, $build); +// +// /** Trigger Webhook */ +// $deploymentUpdate = $queueForEvents +// ->setQueue(Event::WEBHOOK_QUEUE_NAME) +// ->setClass(Event::WEBHOOK_CLASS_NAME) +// ->setProject($project) +// ->setEvent('functions.[functionId].deployments.[deploymentId].update') +// ->setParam('functionId', $function->getId()) +// ->setParam('deploymentId', $deployment->getId()) +// ->setPayload($deployment->getArrayCopy( +// array_keys( +// (new Deployment())->getRules() +// ) +// )); +// +// $deploymentUpdate->trigger(); +// +// /** Trigger Functions */ +// $queueForFunctions +// ->from($deploymentUpdate) +// ->trigger(); +// +// +// /** Trigger Realtime */ +// $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ +// 'functionId' => $function->getId(), +// 'deploymentId' => $deployment->getId() +// ]); +// $target = Realtime::fromPayload( +// // Pass first, most verbose event pattern +// event: $allEvents[0], +// payload: $build, +// project: $project +// ); +// +// Realtime::send( +// projectId: 'console', +// payload: $build->getArrayCopy(), +// events: $allEvents, +// channels: $target['channels'], +// roles: $target['roles'] +// ); +// +// $source = $deployment->getAttribute('path'); +// +// $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { +// $carry[$var->getAttribute('key')] = $var->getAttribute('value'); +// return $carry; +// }, []); +// +// try { +// $response = $executor->createRuntime( +// deploymentId: $deployment->getId(), +// projectId: $project->getId(), +// source: $source, +// image: $runtime['image'], +// remove: true, +// entrypoint: $deployment->getAttribute('entrypoint'), +// workdir: '/usr/code', +// destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", +// variables: $vars, +// commands: [ +// 'sh', '-c', +// 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ +// cd /usr/local/src/ && ./build.sh' +// ] +// ); +// +// $endTime = new \DateTime(); +// $endTime->setTimestamp($response['endTimeUnix']); +// +// /** Update the build document */ +// $build->setAttribute('endTime', DateTime::format($endTime)); +// $build->setAttribute('duration', \intval($response['duration'])); +// $build->setAttribute('status', $response['status']); +// $build->setAttribute('outputPath', $response['outputPath']); +// $build->setAttribute('stderr', $response['stderr']); +// $build->setAttribute('stdout', $response['stdout']); +// +// /* Also update the deployment buildTime */ +// $deployment->setAttribute('buildTime', $response['duration']); +// +// Console::success("Build id: $buildId created"); +// +// /** Set auto deploy */ +// if ($deployment->getAttribute('activate') === true) { +// $function->setAttribute('deploymentInternalId', $deployment->getInternalId()); +// $function->setAttribute('deployment', $deployment->getId()); +// $function = $dbForProject->updateDocument('functions', $function->getId(), $function); +// } +// +// /** Update function schedule */ +// $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); +// $schedule->setAttribute('resourceUpdatedAt', DateTime::now()); +// +// $schedule +// ->setAttribute('schedule', $function->getAttribute('schedule')) +// ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); +// +// +// \Utopia\Database\Validator\Authorization::skip(fn() => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); +// } catch (\Throwable $th) { +// $endTime = DateTime::now(); +// $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); +// $build->setAttribute('endTime', $endTime); +// $build->setAttribute('duration', $interval->format('%s') + 0); +// $build->setAttribute('status', 'failed'); +// $build->setAttribute('stderr', $th->getMessage()); +// Console::error($th->getMessage()); +// } finally { +// $build = $dbForProject->updateDocument('builds', $buildId, $build); +// +// /** +// * Send realtime Event +// */ +// $target = Realtime::fromPayload( +// // Pass first, most verbose event pattern +// event: $allEvents[0], +// payload: $build, +// project: $project +// ); +// Realtime::send( +// projectId: 'console', +// payload: $build->getArrayCopy(), +// events: $allEvents, +// channels: $target['channels'], +// roles: $target['roles'] +// ); +// +// /** Trigger usage queue */ +// $queueForUsage +// ->setProject($project) +// ->addMetric(METRIC_BUILDS, 1) // per project +// ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) +// ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) +// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function +// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) +// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) +// ->trigger(); +// } +// } +//} diff --git a/src/Appwrite/Platform/Workers/UsageHook.php b/src/Appwrite/Platform/Workers/UsageHook.php index 06d5cb9feb..f0bd0ac2c9 100644 --- a/src/Appwrite/Platform/Workers/UsageHook.php +++ b/src/Appwrite/Platform/Workers/UsageHook.php @@ -89,12 +89,6 @@ class UsageHook extends Usage } } } - if (!empty($project['keys'])) { - $dbForProject->createDocument('statsLogger', new Document([ - 'time' => DateTime::now(), - 'metrics' => $project['keys'], - ])); - } } catch (\Exception $e) { console::error("[logger] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}"); } finally { From 9cf83151cd4660b766a0505f0a18d5e37590cfd1 Mon Sep 17 00:00:00 2001 From: shimon Date: Thu, 28 Sep 2023 20:37:07 +0300 Subject: [PATCH 045/144] certificates worker --- src/Appwrite/Platform/Workers/Builds.php | 239 +------------ .../Platform/Workers/Certificates.php | 181 ++++++---- src/Appwrite/Platform/Workers/Functions.php | 326 ++++++++++++------ 3 files changed, 347 insertions(+), 399 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 6dbcac637e..84a431f939 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -21,9 +21,9 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; -use Utopia\DSN\DSN; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; use Utopia\VCS\Adapter\Git\GitHub; @@ -44,19 +44,19 @@ class Builds extends Action ->desc('Builds worker') ->inject('message') ->inject('dbForConsole') - ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForFunctions') ->inject('queueForUsage') ->inject('cache') ->inject('getProjectDB') - ->callback(fn($message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB) => $this->action($message, $dbForConsole, $dbForProject, $queueForEvents, $queueForFunctions, $queueForUsage, $cache, $getProjectDB)); + ->inject('deviceFunctions') + ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $deviceFunctions) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $queueForUsage, $cache, $getProjectDB, $deviceFunctions)); } /** * @throws Exception|\Throwable */ - public function action(Message $message, Database $dbForConsole, Database $dbForProject, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $deviceFunctions): void { $payload = $message->getPayload() ?? []; @@ -75,7 +75,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($queueForEvents, $queueForUsage, $getProjectDB, $dbForConsole, $github, $project, $resource, $deployment, $template); + $this->buildDeployment($deviceFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $getProjectDB, $github, $project, $resource, $deployment, $template); break; default: @@ -88,7 +88,7 @@ class Builds extends Action * @throws \Throwable * @throws Structure */ - protected function buildDeployment(Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template) + protected function buildDeployment(callable $deviceFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template) { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); @@ -116,7 +116,6 @@ class Builds extends Action throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - // Realtime preparation $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ 'functionId' => $function->getId(), @@ -125,9 +124,7 @@ class Builds extends Action $startTime = DateTime::now(); $durationStart = \microtime(true); - $buildId = $deployment->getAttribute('buildId', ''); - $isNewBuild = empty($buildId); if ($isNewBuild) { @@ -272,7 +269,7 @@ class Builds extends Action Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); - $deviceFunctions = $this->getFunctionsDevice($project->getId()); + $deviceFunctions = $deviceFunctions($project->getId()); $localDevice = new Local(); $buffer = $localDevice->read($tmpPathFile); @@ -303,6 +300,7 @@ class Builds extends Action } /** Trigger Webhook */ + $deploymentModel = new Deployment(); $deploymentUpdate = $queueForEvents ->setQueue(Event::WEBHOOK_QUEUE_NAME) @@ -311,11 +309,8 @@ class Builds extends Action ->setEvent('functions.[functionId].deployments.[deploymentId].update') ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) - ->setPayload($deployment->getArrayCopy( - array_keys( - (new Deployment())->getRules() - ) - )); + ->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules()))) + ; $deploymentUpdate->trigger(); @@ -593,217 +588,3 @@ class Builds extends Action } } } - -// // Realtime preparation -// $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ -// 'functionId' => $function->getId(), -// 'deploymentId' => $deployment->getId() -// ]); -// -// $startTime = DateTime::now(); -// $durationStart = \microtime(true); -// -// -// -// $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); -// $function = $dbForProject->getDocument('functions', $function->getId()); -// -// if ($function->isEmpty()) { -// throw new Exception('Function not found', 404); -// } -// -// $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); -// if ($deployment->isEmpty()) { -// throw new Exception('Deployment not found', 404); -// } -// -// $runtimes = Config::getParam('runtimes', []); -// $key = $function->getAttribute('runtime'); -// $runtime = $runtimes[$key] ?? null; -// if (\is_null($runtime)) { -// throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); -// } -// -// $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); -// /** @TODO : move this to the registry or someplace else */ -// $device = Storage::DEVICE_LOCAL; -// try { -// $dsn = new DSN($connection); -// $device = $dsn->getScheme(); -// } catch (\Exception $e) { -// Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); -// } -// -// $buildId = $deployment->getAttribute('buildId', ''); -// $startTime = DateTime::now(); -// -// if (empty($buildId)) { -// $buildId = ID::unique(); -// $build = $dbForProject->createDocument('builds', new Document([ -// '$id' => $buildId, -// '$permissions' => [], -// 'startTime' => $startTime, -// 'deploymentInternalId' => $deployment->getInternalId(), -// 'deploymentId' => $deployment->getId(), -// 'status' => 'processing', -// 'outputPath' => '', -// 'runtime' => $function->getAttribute('runtime'), -// 'source' => $deployment->getAttribute('path'), -// 'sourceType' => $device, -// 'stdout' => '', -// 'stderr' => '', -// 'duration' => 0 -// ])); -// -// $deployment->setAttribute('buildId', $buildId); -// $deployment->setAttribute('buildInternalId', $build->getInternalId()); -// $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); -// } else { -// $build = $dbForProject->getDocument('builds', $buildId); -// } -// -// /** Request the executor to build the code... */ -// $build->setAttribute('status', 'building'); -// $build = $dbForProject->updateDocument('builds', $buildId, $build); -// -// /** Trigger Webhook */ -// $deploymentUpdate = $queueForEvents -// ->setQueue(Event::WEBHOOK_QUEUE_NAME) -// ->setClass(Event::WEBHOOK_CLASS_NAME) -// ->setProject($project) -// ->setEvent('functions.[functionId].deployments.[deploymentId].update') -// ->setParam('functionId', $function->getId()) -// ->setParam('deploymentId', $deployment->getId()) -// ->setPayload($deployment->getArrayCopy( -// array_keys( -// (new Deployment())->getRules() -// ) -// )); -// -// $deploymentUpdate->trigger(); -// -// /** Trigger Functions */ -// $queueForFunctions -// ->from($deploymentUpdate) -// ->trigger(); -// -// -// /** Trigger Realtime */ -// $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ -// 'functionId' => $function->getId(), -// 'deploymentId' => $deployment->getId() -// ]); -// $target = Realtime::fromPayload( -// // Pass first, most verbose event pattern -// event: $allEvents[0], -// payload: $build, -// project: $project -// ); -// -// Realtime::send( -// projectId: 'console', -// payload: $build->getArrayCopy(), -// events: $allEvents, -// channels: $target['channels'], -// roles: $target['roles'] -// ); -// -// $source = $deployment->getAttribute('path'); -// -// $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { -// $carry[$var->getAttribute('key')] = $var->getAttribute('value'); -// return $carry; -// }, []); -// -// try { -// $response = $executor->createRuntime( -// deploymentId: $deployment->getId(), -// projectId: $project->getId(), -// source: $source, -// image: $runtime['image'], -// remove: true, -// entrypoint: $deployment->getAttribute('entrypoint'), -// workdir: '/usr/code', -// destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", -// variables: $vars, -// commands: [ -// 'sh', '-c', -// 'tar -zxf /tmp/code.tar.gz -C /usr/code && \ -// cd /usr/local/src/ && ./build.sh' -// ] -// ); -// -// $endTime = new \DateTime(); -// $endTime->setTimestamp($response['endTimeUnix']); -// -// /** Update the build document */ -// $build->setAttribute('endTime', DateTime::format($endTime)); -// $build->setAttribute('duration', \intval($response['duration'])); -// $build->setAttribute('status', $response['status']); -// $build->setAttribute('outputPath', $response['outputPath']); -// $build->setAttribute('stderr', $response['stderr']); -// $build->setAttribute('stdout', $response['stdout']); -// -// /* Also update the deployment buildTime */ -// $deployment->setAttribute('buildTime', $response['duration']); -// -// Console::success("Build id: $buildId created"); -// -// /** Set auto deploy */ -// if ($deployment->getAttribute('activate') === true) { -// $function->setAttribute('deploymentInternalId', $deployment->getInternalId()); -// $function->setAttribute('deployment', $deployment->getId()); -// $function = $dbForProject->updateDocument('functions', $function->getId(), $function); -// } -// -// /** Update function schedule */ -// $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); -// $schedule->setAttribute('resourceUpdatedAt', DateTime::now()); -// -// $schedule -// ->setAttribute('schedule', $function->getAttribute('schedule')) -// ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); -// -// -// \Utopia\Database\Validator\Authorization::skip(fn() => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); -// } catch (\Throwable $th) { -// $endTime = DateTime::now(); -// $interval = (new \DateTime($endTime))->diff(new \DateTime($startTime)); -// $build->setAttribute('endTime', $endTime); -// $build->setAttribute('duration', $interval->format('%s') + 0); -// $build->setAttribute('status', 'failed'); -// $build->setAttribute('stderr', $th->getMessage()); -// Console::error($th->getMessage()); -// } finally { -// $build = $dbForProject->updateDocument('builds', $buildId, $build); -// -// /** -// * Send realtime Event -// */ -// $target = Realtime::fromPayload( -// // Pass first, most verbose event pattern -// event: $allEvents[0], -// payload: $build, -// project: $project -// ); -// Realtime::send( -// projectId: 'console', -// payload: $build->getArrayCopy(), -// events: $allEvents, -// channels: $target['channels'], -// roles: $target['roles'] -// ); -// -// /** Trigger usage queue */ -// $queueForUsage -// ->setProject($project) -// ->addMetric(METRIC_BUILDS, 1) // per project -// ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) -// ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) -// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function -// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) -// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) -// ->trigger(); -// } -// } -//} diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index df60343078..4c9eb0bbaf 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -2,9 +2,13 @@ namespace Appwrite\Platform\Workers; +use Appwrite\Event\Event; +use Appwrite\Event\Func; use Appwrite\Event\Mail; +use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\CNAME; use Appwrite\Template\Template; +use Appwrite\Utopia\Response\Model\Rule; use Exception; use Throwable; use Utopia\App; @@ -36,13 +40,15 @@ class Certificates extends Action ->inject('message') ->inject('dbForConsole') ->inject('queueForMails') - ->callback(fn($message, $dbForConsole, $queueForMails) => $this->action($message, $dbForConsole, $queueForMails)); + ->inject('queueForEvents') + ->inject('queueForFunctions') + ->callback(fn(Message $message, Database $dbForConsole, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions) => $this->action($message, $dbForConsole, $queueForMails, $queueForEvents, $queueForFunctions)); } /** * @throws Exception|Throwable */ - public function action(Message $message, Database $dbForConsole, Mail $queueForMails): void + public function action(Message $message, Database $dbForConsole, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions): void { $payload = $message->getPayload() ?? []; @@ -54,13 +60,13 @@ class Certificates extends Action $domain = new Domain($document->getAttribute('domain', '')); $skipRenewCheck = $payload['skipRenewCheck'] ?? false; - $this->execute($domain, $dbForConsole, $queueForMails, $skipRenewCheck); + $this->execute($domain, $dbForConsole, $queueForMails, $queueForEvents, $queueForFunctions, $skipRenewCheck); } /** * @throws Exception|Throwable */ - private function execute(Domain $domain, Database $dbForConsole, Mail $queueForMails, bool $skipRenewCheck = false): void + private function execute(Domain $domain, Database $dbForConsole, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, bool $skipRenewCheck = false): void { /** * 1. Read arguments and validate domain @@ -101,6 +107,8 @@ class Certificates extends Action $certificate->setAttribute('domain', $domain->get()); } + $success = false; + try { // Email for alerts is required by LetsEncrypt $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); @@ -127,11 +135,9 @@ class Certificates extends Action $letsEncryptData = $this->issueCertificate($folder, $domain->get(), $email); // Command succeeded, store all data into document - // We store stderr too, because it may include warnings - $certificate->setAttribute('log', \json_encode([ - 'stdout' => $letsEncryptData['stdout'], - 'stderr' => $letsEncryptData['stderr'], - ])); + $logs = 'Certificate successfully generated.'; + $certificate->setAttribute('logs', \mb_strcut($logs, 0, 1000000));// Limit to 1MB + // Give certificates to Traefik $this->applyCertificateFiles($folder, $domain->get(), $letsEncryptData); @@ -140,9 +146,12 @@ class Certificates extends Action $certificate->setAttribute('renewDate', $this->getRenewDate($domain->get())); $certificate->setAttribute('attempts', 0); $certificate->setAttribute('issueDate', DateTime::now()); + $success = true; } catch (Throwable $e) { + $logs = $e->getMessage(); + // Set exception as log in certificate document - $certificate->setAttribute('log', $e->getMessage()); + $certificate->setAttribute('logs', \mb_strcut($logs, 0, 1000000));// Limit to 1MB // Increase attempts count $attempts = $certificate->getAttribute('attempts', 0) + 1; @@ -158,26 +167,45 @@ class Certificates extends Action $certificate->setAttribute('updated', DateTime::now()); // Save all changes we made to certificate document into database - $this->saveCertificateDocument($domain->get(), $certificate, $dbForConsole); + $this->saveCertificateDocument($domain->get(), $certificate, $success, $dbForConsole, $queueForEvents, $queueForFunctions); } } + /** + * Save certificate data into database. + * + * @param string $domain Domain name that certificate is for + * @param Document $certificate Certificate document that we need to save + * @param Database $dbForConsole Database connection for console + * @return void + * @throws Exception|Throwable + */ + private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions): void + { + // Check if update or insert required + $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); + if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { + // Merge new data with current data + $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); + $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); + } else { + $certificate = $dbForConsole->createDocument('certificates', $certificate); + } + + $certificateId = $certificate->getId(); + $this->updateDomainDocuments($certificateId, $domain, $success, $dbForConsole, $queueForEvents, $queueForFunctions); + } + /** * Get main domain. Needed as we do different checks for main and non-main domains. * * @return null|string Returns main domain. If null, there is no main domain yet. - * @throws Exception */ private function getMainDomain(Database $dbForConsole): ?string { $envDomain = App::getEnv('_APP_DOMAIN', ''); if (!empty($envDomain) && $envDomain !== 'localhost') { return $envDomain; - } else { - $domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]); - if ($domainDocument) { - return $domainDocument->getAttribute('domain'); - } } return null; @@ -190,6 +218,7 @@ class Certificates extends Action * * @param Domain $domain Domain which we validate * @param bool $isMainDomain In case of master domain, we look for different DNS configurations + * * @return void * @throws Exception */ @@ -267,7 +296,7 @@ class Certificates extends Action $stderr = ''; $staging = (App::isProduction()) ? '' : ' --dry-run'; - $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + $exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}" . " --email " . $email . " --cert-name " . $folder . " -w " . APP_STORAGE_CERTIFICATES @@ -284,6 +313,22 @@ class Certificates extends Action ]; } + /** + * Read new renew date from certificate file generated by Let's Encrypt + * + * @param string $domain Domain which certificate was generated for + * @return string + * @throws \Utopia\Database\Exception + */ + private function getRenewDate(string $domain): string + { + $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; + $certData = openssl_x509_parse(file_get_contents($certPath)); + $validTo = $certData['validTo_time_t'] ?? null; + $dt = (new \DateTime())->setTimestamp($validTo); + return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days + } + /** * Method to take files from Let's Encrypt, and put it into Traefik. * @@ -349,19 +394,20 @@ class Certificates extends Action // Log error into console Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); - // Send mail to administrator mail + // Send mail to administratore mail + $locale = new Locale(App::getEnv('_APP_LOCALE', 'en')); if (!$locale->getText('emails.sender') || !$locale->getText("emails.certificate.hello") || !$locale->getText("emails.certificate.subject") || !$locale->getText("emails.certificate.body") || !$locale->getText("emails.certificate.footer") || !$locale->getText("emails.certificate.thanks") || !$locale->getText("emails.certificate.signature")) { $locale->setDefault('en'); } - $body = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-base.tpl'); + $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); $subject = \sprintf($locale->getText("emails.certificate.subject"), $domain); - $body->setParam('{{domain}}', $domain); - $body->setParam('{{error}}', $errorMessage); - $body->setParam('{{attempt}}', $attempt); $body + ->setParam('{{domain}}', $domain) + ->setParam('{{error}}', $errorMessage) + ->setParam('{{attempt}}', $attempt) ->setParam('{{subject}}', $subject) ->setParam('{{hello}}', $locale->getText("emails.certificate.hello")) ->setParam('{{body}}', $locale->getText("emails.certificate.body")) @@ -391,50 +437,69 @@ class Certificates extends Action * * @param string $certificateId ID of a new or updated certificate document * @param string $domain Domain that is affected by new certificate - * @param Database $dbForConsole Database instance for console + * @param bool $success Was certificate generation successful? + * * @return void - * @throws Exception */ - private function updateDomainDocuments(string $certificateId, string $domain, Database $dbForConsole): void + private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions): void { - $domains = $dbForConsole->find('domains', [ + + $rule = $dbForConsole->findOne('rules', [ Query::equal('domain', [$domain]), - Query::limit(1000), ]); - foreach ($domains as $domainDocument) { - $domainDocument->setAttribute('updated', DateTime::now()); - $domainDocument->setAttribute('certificateId', $certificateId); - $dbForConsole->updateDocument('domains', $domainDocument->getId(), $domainDocument); + if ($rule !== false && !$rule->isEmpty()) { + $rule->setAttribute('certificateId', $certificateId); + $rule->setAttribute('status', $success ? 'verified' : 'unverified'); + $dbForConsole->updateDocument('rules', $rule->getId(), $rule); - if ($domainDocument->getAttribute('projectId')) { - $dbForConsole->deleteCachedDocument('projects', $domainDocument->getAttribute('projectId')); + $projectId = $rule->getAttribute('projectId'); + + // Skip events for console project (triggered by auto-ssl generation for 1 click setups) + if ($projectId === 'console') { + return; } - } - } - /** - * Save certificate data into database. - * - * @param string $domain Domain name that certificate is for - * @param Document $certificate Certificate document that we need to save - * @param Database $dbForConsole Database connection for console - * @return void - * @throws Exception|\Throwable - */ - private function saveCertificateDocument(string $domain, Document $certificate, Database $dbForConsole): void - { - // Check if update or insert required - $certificateDocument = $dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); - if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { - // Merge new data with current data - $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); - $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); - } else { - $certificate = $dbForConsole->createDocument('certificates', $certificate); - } + $project = $dbForConsole->getDocument('projects', $projectId); - $certificateId = $certificate->getId(); - $this->updateDomainDocuments($certificateId, $domain, $dbForConsole); + /** Trigger Webhook */ + $ruleModel = new Rule(); + $queueForEvents + ->setProject($project) + ->setEvent('rules.[ruleId].update') + ->setParam('ruleId', $rule->getId()) + ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) + ->trigger(); + + + /** Trigger Functions */ + $queueForFunctions + ->trigger(); + + /** Trigger realtime event */ + $allEvents = Event::generateEvents('rules.[ruleId].update', [ + 'ruleId' => $rule->getId(), + ]); + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $rule, + project: $project + ); + Realtime::send( + projectId: 'console', + payload: $rule->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + Realtime::send( + projectId: $project->getId(), + payload: $rule->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + } } } diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index d3c4399323..695d1c7486 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -9,6 +9,7 @@ use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; +use Throwable; use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -20,6 +21,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -42,13 +44,14 @@ class Functions extends Action ->inject('queueForFunctions') ->inject('queueForEvents') ->inject('queueForUsage') - ->callback(fn($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage)); + ->inject('log') + ->callback(fn(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log)); } /** - * @throws Exception|\Throwable + * @throws Exception|Throwable */ - public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage): void + public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log): void { $payload = $message->getPayload() ?? []; @@ -69,6 +72,9 @@ class Functions extends Action $project = new Document($payload['project'] ?? []); $function = new Document($payload['function'] ?? []); $user = new Document($payload['user'] ?? []); + $method = $payload['method'] ?? 'POST'; + $headers = $payload['headers'] ?? []; + $path = $payload['path'] ?? '/'; if ($project->getId() === 'console') { return; @@ -99,16 +105,25 @@ class Functions extends Action Console::success('Iterating function: ' . $function->getAttribute('name')); $this->execute( + log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForEvents: $queueForEvents, queueForUsage: $queueForUsage, + queueForEvents: $queueForEvents, project: $project, function: $function, trigger: 'event', + path: '/', + method: 'POST', + headers: [ + 'user-agent' => 'Appwrite/' . APP_VERSION_STABLE + ], + data: null, user: $user, + jwt: null, event: $events[0], eventData: \is_string($eventData) ? $eventData : \json_encode($eventData), + executionId: null, ); Console::success('Triggered function: ' . $events[0]); } @@ -125,28 +140,44 @@ class Functions extends Action $execution = new Document($payload['execution'] ?? []); $user = new Document($payload['user'] ?? []); $this->execute( + log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForEvents: $queueForEvents, queueForUsage: $queueForUsage, + queueForEvents: $queueForEvents, project: $project, function: $function, trigger: 'http', + path: $path, + method: $method, + headers: $headers, data: $data, user: $user, jwt: $jwt, - executionId: $execution->getId(), + event: null, + eventData: null, + executionId: $execution->getId() ); break; case 'schedule': $this->execute( + log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForEvents: $queueForEvents, queueForUsage: $queueForUsage, + queueForEvents: $queueForEvents, project: $project, function: $function, trigger: 'schedule', + path: $path, + method: $method, + headers: $headers, + data: null, + user: null, + jwt: null, + event: null, + eventData: null, + executionId: null, ); break; } @@ -154,17 +185,21 @@ class Functions extends Action /** * @throws Authorization - * @throws \Throwable + * @throws Throwable * @throws Structure */ private function execute( + Log $log, Database $dbForProject, Func $queueForFunctions, - Event $queueForEvents, Usage $queueForUsage, + Event $queueForEvents, Document $project, Document $function, string $trigger, + string $path, + string $method, + array $headers, string $data = null, ?Document $user = null, string $jwt = null, @@ -177,6 +212,9 @@ class Functions extends Action $functionInternalId = $function->getInternalId(); $deploymentId = $function->getAttribute('deployment', ''); + $log->addTag('functionId', $functionId); + $log->addTag('projectId', $project->getId()); + /** Check if deployment exists */ $deployment = $dbForProject->getDocument('deployments', $deploymentId); $deploymentInternalId = $deployment->getInternalId(); @@ -200,157 +238,221 @@ class Functions extends Action } /** Check if runtime is supported */ - $runtimes = Config::getParam('runtimes', []); + $version = $function->getAttribute('version', 'v2'); + $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); if (!\array_key_exists($function->getAttribute('runtime'), $runtimes)) { throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $runtime = $runtimes[$function->getAttribute('runtime')]; + $runtime = $runtimes[$function->getAttribute('runtime')]; - /** Create execution or update execution status */ - $execution = $dbForProject->getDocument('executions', $executionId ?? ''); + $headers['x-appwrite-trigger'] = $trigger; + $headers['x-appwrite-event'] = $event ?? ''; + $headers['x-appwrite-user-id'] = $user->getId() ?? ''; + $headers['x-appwrite-user-jwt'] = $jwt ?? ''; + + /** Create execution or update execution status */ + /** Create execution or update execution status */ + $execution = $dbForProject->getDocument('executions', $executionId ?? ''); if ($execution->isEmpty()) { + $headersFiltered = []; + foreach ($headers as $key => $value) { + if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) { + $headersFiltered[] = [ 'name' => $key, 'value' => $value ]; + } + } + $executionId = ID::unique(); - $execution = $dbForProject->createDocument('executions', new Document([ + $execution = new Document([ '$id' => $executionId, '$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))], - 'functionId' => $functionId, - 'functionInternalId' => $functionInternalId, - 'deploymentInternalId' => $deploymentInternalId, - 'deploymentId' => $deploymentId, + 'functionInternalId' => $function->getInternalId(), + 'functionId' => $function->getId(), + 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentId' => $deployment->getId(), 'trigger' => $trigger, - 'status' => 'waiting', - 'statusCode' => 0, - 'response' => '', - 'stderr' => '', + 'status' => 'processing', + 'responseStatusCode' => 0, + 'responseHeaders' => [], + 'requestPath' => $path, + 'requestMethod' => $method, + 'requestHeaders' => $headersFiltered, + 'errors' => '', + 'logs' => '', 'duration' => 0.0, - 'search' => implode(' ', [$function->getId(), $executionId]), - ])); + 'search' => implode(' ', [$functionId, $executionId]), + ]); + + if ($function->getAttribute('logging')) { + $execution = $dbForProject->createDocument('executions', $execution); + } // TODO: @Meldiron Trigger executions.create event here if ($execution->isEmpty()) { throw new Exception('Failed to create or read execution'); } - - /*** Usage */ - $queueForUsage - ->addMetric(METRIC_EXECUTIONS, 1) // per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1); // per function } + if ($execution->getAttribute('status') !== 'processing') { $execution->setAttribute('status', 'processing'); - $execution = $dbForProject->updateDocument('executions', $executionId, $execution); - $vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) { - $carry[$var->getAttribute('key')] = $var->getAttribute('value'); - return $carry; - }, []); + if ($function->getAttribute('logging')) { + $execution = $dbForProject->updateDocument('executions', $executionId, $execution); + } + } - /** Collect environment variables */ + $durationStart = \microtime(true); + + $body = $eventData ?? ''; + if (empty($body)) { + $body = $data ?? ''; + } + + $vars = []; + + // V2 vars + if ($version === 'v2') { $vars = \array_merge($vars, [ - 'APPWRITE_FUNCTION_ID' => $functionId, - 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), - 'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId, - 'APPWRITE_FUNCTION_TRIGGER' => $trigger, - 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), - 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', - 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - 'APPWRITE_FUNCTION_EVENT' => $event ?? '', - 'APPWRITE_FUNCTION_EVENT_DATA' => $eventData ?? '', - 'APPWRITE_FUNCTION_DATA' => $data ?? '', - 'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '', - 'APPWRITE_FUNCTION_JWT' => $jwt ?? '', + 'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '', + 'APPWRITE_FUNCTION_DATA' => $body ?? '', + 'APPWRITE_FUNCTION_EVENT_DATA' => $body ?? '', + 'APPWRITE_FUNCTION_EVENT' => $headers['x-appwrite-event'] ?? '', + 'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '', + 'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? '' ]); + } - /** Execute function */ + // Shared vars + foreach ($function->getAttribute('varsProject', []) as $var) { + $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); + } + + // Function vars + foreach ($function->getAttribute('vars', []) as $var) { + $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); + } + + // Appwrite vars + $vars = \array_merge($vars, [ + 'APPWRITE_FUNCTION_ID' => $functionId, + 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), + 'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId, + 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', + ]); + + /** Execute function */ try { - $client = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); - $executionResponse = $client->createExecution( + $version = $function->getAttribute('version', 'v2'); + $command = $runtime['startCommand']; + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; + $executionResponse = $executor->createExecution( projectId: $project->getId(), deploymentId: $deploymentId, - payload: $vars['APPWRITE_FUNCTION_DATA'] ?? '', + body: \strlen($body) > 0 ? $body : null, variables: $vars, timeout: $function->getAttribute('timeout', 0), image: $runtime['image'], - source: $build->getAttribute('outputPath', ''), + source: $build->getAttribute('path', ''), entrypoint: $deployment->getAttribute('entrypoint', ''), + version: $version, + path: $path, + method: $method, + headers: $headers, + runtimeEntrypoint: $command ); + $status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed'; + + $headersFiltered = []; + foreach ($executionResponse['headers'] as $key => $value) { + if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { + $headersFiltered[] = [ 'name' => $key, 'value' => $value ]; + } + } + /** Update execution status */ $execution - ->setAttribute('status', $executionResponse['status']) - ->setAttribute('statusCode', $executionResponse['statusCode']) - ->setAttribute('response', $executionResponse['response']) - ->setAttribute('stdout', $executionResponse['stdout']) - ->setAttribute('stderr', $executionResponse['stderr']) + ->setAttribute('status', $status) + ->setAttribute('responseStatusCode', $executionResponse['statusCode']) + ->setAttribute('responseHeaders', $headersFiltered) + ->setAttribute('logs', $executionResponse['logs']) + ->setAttribute('errors', $executionResponse['errors']) ->setAttribute('duration', $executionResponse['duration']); } catch (\Throwable $th) { - $interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt())); + $durationEnd = \microtime(true); $execution - ->setAttribute('duration', (float)$interval->format('%s.%f')) + ->setAttribute('duration', $durationEnd - $durationStart) ->setAttribute('status', 'failed') - ->setAttribute('statusCode', $th->getCode()) - ->setAttribute('stderr', $th->getMessage()); + ->setAttribute('responseStatusCode', 500) + ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); - Console::error($th->getTraceAsString()); - Console::error($th->getFile()); - Console::error($th->getLine()); - Console::error($th->getMessage()); + $error = $th->getMessage(); + $errorCode = $th->getCode(); } + if ($function->getAttribute('logging')) { $execution = $dbForProject->updateDocument('executions', $executionId, $execution); + } + /** Trigger Webhook */ + $executionModel = new Execution(); + $queueForEvents + ->setQueue(Event::WEBHOOK_QUEUE_NAME) + ->setClass(Event::WEBHOOK_CLASS_NAME) + ->setProject($project) + ->setUser($user) + ->setEvent('functions.[functionId].executions.[executionId].update') + ->setParam('functionId', $function->getId()) + ->setParam('executionId', $execution->getId()) + ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))) + ->trigger(); - /** Trigger Webhook */ - $executionModel = new Execution(); - $queueForEvents - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setProject($project) - ->setUser($user) - ->setEvent('functions.[functionId].executions.[executionId].update') - ->setParam('functionId', $function->getId()) - ->setParam('executionId', $execution->getId()) - ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))) - ->trigger(); + /** Trigger Functions */ + $queueForFunctions + ->from($queueForEvents) + ->trigger(); - /** Trigger Functions */ - $queueForFunctions - ->from($queueForEvents) - ->trigger(); + /** Trigger realtime event */ + $allEvents = Event::generateEvents('functions.[functionId].executions.[executionId].update', [ + 'functionId' => $function->getId(), + 'executionId' => $execution->getId() + ]); + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $allEvents[0], + payload: $execution + ); + Realtime::send( + projectId: 'console', + payload: $execution->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); + Realtime::send( + projectId: $project->getId(), + payload: $execution->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'] + ); - /** Trigger realtime event */ - $allEvents = Event::generateEvents('functions.[functionId].executions.[executionId].update', [ - 'functionId' => $function->getId(), - 'executionId' => $execution->getId() - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $execution - ); - Realtime::send( - projectId: 'console', - payload: $execution->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $execution->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + if (!empty($error)) { + throw new Exception($error, $errorCode); + } - /** Trigger usage queue */ - $queueForUsage - ->setProject($project) - ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) - ->trigger() - ; + /** Trigger usage queue */ + $queueForUsage + ->setProject($project) + ->addMetric(METRIC_EXECUTIONS, 1) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) + ->trigger() + ; } } From 47d27096dbf9166103a76e5c3006b9d256997d48 Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 1 Oct 2023 11:04:12 +0300 Subject: [PATCH 046/144] deletes & databases workers --- src/Appwrite/Platform/Workers/Databases.php | 247 ++++++++-- src/Appwrite/Platform/Workers/Deletes.php | 520 ++++++++++++++------ 2 files changed, 588 insertions(+), 179 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index dfa22988f2..79df4b3409 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -9,6 +9,9 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Conflict; +use Utopia\Database\Exception\Structure; +use Utopia\Database\Exception as DatabaseException; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -33,6 +36,9 @@ class Databases extends Action } /** + * @param Message $message + * @param Database $dbForConsole + * @param Database $dbForProject * @throws Exception */ public function action(Message $message, Database $dbForConsole, Database $dbForProject): void @@ -58,17 +64,30 @@ class Databases extends Action } match (strval($type)) { - DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForProject), + DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForConsole, $dbForProject), DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForConsole, $dbForProject), - DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForProject), - DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForProject), + DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForConsole, $dbForProject), + DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForConsole, $dbForProject), default => Console::error('No database operation for type: ' . $type), }; } - private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, $dbForProject): void + /** + * @param Document $database + * @param Document $collection + * @param Document $attribute + * @param Document $project + * @param Database $dbForConsole + * @param Database $dbForProject + * @throws Authorization + * @throws Conflict + * @throws Exception + */ + private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void { + $projectId = $project->getId(); + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ 'databaseId' => $database->getId(), 'collectionId' => $collection->getId(), @@ -78,6 +97,7 @@ class Databases extends Action * Fetch attribute from the database, since with Resque float values are loosing informations. */ $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); + $collectionId = $collection->getId(); $key = $attribute->getAttribute('key', ''); $type = $attribute->getAttribute('type', ''); @@ -89,15 +109,67 @@ class Databases extends Action $format = $attribute->getAttribute('format', ''); $formatOptions = $attribute->getAttribute('formatOptions', []); $filters = $attribute->getAttribute('filters', []); + $options = $attribute->getAttribute('options', []); + $project = $dbForConsole->getDocument('projects', $projectId); + try { - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); + switch ($type) { + case Database::VAR_RELATIONSHIP: + $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedCollection->isEmpty()) { + throw new DatabaseException('Collection not found'); + } + + if ( + !$dbForProject->createRelationship( + collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + type: $options['relationType'], + twoWay: $options['twoWay'], + id: $key, + twoWayKey: $options['twoWayKey'], + onDelete: $options['onDelete'], + ) + ) { + throw new DatabaseException('Failed to create Attribute'); + } + + if ($options['twoWay']) { + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available')); + } + break; + default: + if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + throw new Exception('Failed to create Attribute'); + } } + $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); + } catch (\Exception $e) { + Console::error($e->getMessage()); + + if ($e instanceof DatabaseException) { + $attribute->setAttribute('error', $e->getMessage()); + if (isset($relatedAttribute)) { + $relatedAttribute->setAttribute('error', $e->getMessage()); + } + } + + $dbForProject->updateDocument( + 'attributes', + $attribute->getId(), + $attribute->setAttribute('status', 'failed') + ); + + if (isset($relatedAttribute)) { + $dbForProject->updateDocument( + 'attributes', + $relatedAttribute->getId(), + $relatedAttribute->setAttribute('status', 'failed') + ); + } } finally { $target = Realtime::fromPayload( // Pass first, most verbose event pattern @@ -113,45 +185,101 @@ class Databases extends Action channels: $target['channels'], roles: $target['roles'], options: [ - 'projectId' => $project->getId(), + 'projectId' => $projectId, 'databaseId' => $database->getId(), 'collectionId' => $collection->getId() ] ); } + if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + } + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); } /** + * @param Document $database + * @param Document $collection + * @param Document $attribute + * @param Document $project + * @param Database $dbForConsole + * @param Database $dbForProject * @throws Authorization - */ + * @throws Conflict + * @throws Exception + **/ private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject) { + $projectId = $project->getId(); + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ 'databaseId' => $database->getId(), 'collectionId' => $collection->getId(), 'attributeId' => $attribute->getId() ]); - $collectionId = $collection->getId(); $key = $attribute->getAttribute('key', ''); $status = $attribute->getAttribute('status', ''); - + $type = $attribute->getAttribute('type', ''); + $project = $dbForConsole->getDocument('projects', $projectId); + $options = $attribute->getAttribute('options', []); + $relatedAttribute = new Document(); + $relatedCollection = new Document(); // possible states at this point: // - available: should not land in queue; controller flips these to 'deleting' // - processing: hasn't finished creating // - deleting: was available, in deletion queue for first time // - failed: attribute was never created // - stuck: attribute was available but cannot be removed + try { - if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new Exception('Failed to delete Attribute'); + if ($status !== 'failed') { + if ($type === Database::VAR_RELATIONSHIP) { + if ($options['twoWay']) { + $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + if ($relatedCollection->isEmpty()) { + throw new DatabaseException('Collection not found'); + } + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + } + + if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); + throw new DatabaseException('Failed to delete Relationship'); + } + } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + throw new DatabaseException('Failed to delete Attribute'); + } } + $dbForProject->deleteDocument('attributes', $attribute->getId()); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck')); + + if (!$relatedAttribute->isEmpty()) { + $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); + } + } catch (\Exception $e) { + Console::error($e->getMessage()); + + if ($e instanceof DatabaseException) { + $attribute->setAttribute('error', $e->getMessage()); + if (!$relatedAttribute->isEmpty()) { + $relatedAttribute->setAttribute('error', $e->getMessage()); + } + } + $dbForProject->updateDocument( + 'attributes', + $attribute->getId(), + $attribute->setAttribute('status', 'stuck') + ); + if (!$relatedAttribute->isEmpty()) { + $dbForProject->updateDocument( + 'attributes', + $relatedAttribute->getId(), + $relatedAttribute->setAttribute('status', 'stuck') + ); + } } finally { $target = Realtime::fromPayload( // Pass first, most verbose event pattern @@ -167,7 +295,7 @@ class Databases extends Action channels: $target['channels'], roles: $target['roles'], options: [ - 'projectId' => $project->getId(), + 'projectId' => $projectId, 'databaseId' => $database->getId(), 'collectionId' => $collection->getId() ] @@ -192,8 +320,8 @@ class Databases extends Action // array_values wraps array_diff to reindex array keys // when found attribute is removed from array $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); - $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); - $orders = \array_values(\array_diff($orders, [$orders[$found]])); + $lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : [])); + $orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : [])); if (empty($attributes)) { $dbForProject->deleteDocument('indexes', $index->getId()); @@ -217,7 +345,7 @@ class Databases extends Action } if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project, $dbForConsole); + $this->deleteIndex($database, $collection, $index, $project); } else { $dbForProject->updateDocument('indexes', $index->getId(), $index); } @@ -227,10 +355,29 @@ class Databases extends Action $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + + if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) { + $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + } } - private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForProject) + /** + * @param Document $database + * @param Document $collection + * @param Document $index + * @param Document $project + * @param Database $dbForConsole + * @param Database $dbForProject + * @throws Authorization + * @throws Conflict + * @throws Structure + * @throws DatabaseException + */ + private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject) { + $projectId = $project->getId(); + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ 'databaseId' => $database->getId(), 'collectionId' => $collection->getId(), @@ -242,15 +389,24 @@ class Databases extends Action $attributes = $index->getAttribute('attributes', []); $lengths = $index->getAttribute('lengths', []); $orders = $index->getAttribute('orders', []); + $project = $dbForConsole->getDocument('projects', $projectId); try { if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { - throw new Exception('Failed to create Index'); + throw new DatabaseException('Failed to create Index'); } $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); + } catch (\Exception $e) { + Console::error($e->getMessage()); + + if ($e instanceof DatabaseException) { + $index->setAttribute('error', $e->getMessage()); + } + $dbForProject->updateDocument( + 'indexes', + $index->getId(), + $index->setAttribute('status', 'failed') + ); } finally { $target = Realtime::fromPayload( // Pass first, most verbose event pattern @@ -266,7 +422,7 @@ class Databases extends Action channels: $target['channels'], roles: $target['roles'], options: [ - 'projectId' => $project->getId(), + 'projectId' => $projectId, 'databaseId' => $database->getId(), 'collectionId' => $collection->getId() ] @@ -276,8 +432,22 @@ class Databases extends Action $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); } - private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForProject) + /** + * @param Document $database + * @param Document $collection + * @param Document $index + * @param Document $project + * @param Database $dbForConsole + * @param Database $dbForProject + * @throws Authorization + * @throws Conflict + * @throws Structure + * @throws DatabaseException + */ + private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject) { + $projectId = $project->getId(); + $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ 'databaseId' => $database->getId(), 'collectionId' => $collection->getId(), @@ -285,15 +455,24 @@ class Databases extends Action ]); $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); + $project = $dbForConsole->getDocument('projects', $projectId); try { if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new Exception('Failed to delete index'); + throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck')); + } catch (\Exception $e) { + Console::error($e->getMessage()); + + if ($e instanceof DatabaseException) { + $index->setAttribute('error', $e->getMessage()); + } + $dbForProject->updateDocument( + 'indexes', + $index->getId(), + $index->setAttribute('status', 'stuck') + ); } finally { $target = Realtime::fromPayload( // Pass first, most verbose event pattern @@ -309,7 +488,7 @@ class Databases extends Action channels: $target['channels'], roles: $target['roles'], options: [ - 'projectId' => $project->getId(), + 'projectId' => $projectId, 'databaseId' => $database->getId(), 'collectionId' => $collection->getId() ] diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 3c036d182f..ce00d9c51d 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -2,6 +2,8 @@ namespace Appwrite\Platform\Workers; +use Executor\Executor; +use Throwable; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Audit\Audit; @@ -14,6 +16,9 @@ use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Conflict; +use Utopia\Database\Exception\Restricted; +use Utopia\Database\Exception\Structure; use Utopia\Database\Query; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -35,18 +40,18 @@ class Deletes extends Action ->inject('message') ->inject('dbForConsole') ->inject('getProjectDB') - ->inject('deviceFiles') - ->inject('deviceFunctions') - ->inject('deviceBuilds') - ->inject('deviceCache') - ->callback(fn($message, $dbForConsole, $getProjectDB, $deviceFiles, $deviceFunctions, $deviceBuilds, $deviceCache) => $this->action($message, $dbForConsole, $getProjectDB, $deviceFiles, $deviceFunctions, $deviceBuilds, $deviceCache)); + ->inject('getFilesDevice') + ->inject('getFunctionsDevice') + ->inject('getBuildsDevice') + ->inject('getCacheDevice') + ->callback(fn($message, $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice) => $this->action($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice)); } /** * @throws Exception - * @throws \Throwable + * @throws Throwable */ - public function action(Message $message, Database $dbForConsole, callable $getProjectDB, callable $deviceFiles, callable $deviceFunctions, callable $deviceBuilds, callable $deviceCache): void + public function action(Message $message, Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice): void { $payload = $message->getPayload() ?? []; @@ -71,22 +76,31 @@ class Deletes extends Action $this->deleteCollection($getProjectDB, $document, $project); break; case DELETE_TYPE_PROJECTS: - $this->deleteProject($dbForConsole, $getProjectDB, $deviceFiles, $deviceFunctions, $deviceBuilds, $deviceCache, $document); + $this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document); break; case DELETE_TYPE_FUNCTIONS: - $this->deleteFunction($getProjectDB, $deviceFunctions, $deviceBuilds, $document, $project); + $this->deleteFunction($dbForConsole, $getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); break; case DELETE_TYPE_DEPLOYMENTS: - $this->deleteDeployment($getProjectDB, $deviceFunctions, $deviceBuilds, $document, $project); + $this->deleteDeployment($getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); break; case DELETE_TYPE_USERS: $this->deleteUser($getProjectDB, $document, $project); break; case DELETE_TYPE_TEAMS: - $this->deleteMemberships($getProjectDB, $document, $project); + $this->deleteMemberships($document, $project); + if ($project->getId() === 'console') { + $this->deleteProjectsByTeam($dbForConsole, $document); + } break; case DELETE_TYPE_BUCKETS: - $this->deleteBucket($getProjectDB, $deviceFiles, $document, $project); + $this->deleteBucket($getProjectDB, $getFilesDevice, $document, $project); + break; + case DELETE_TYPE_INSTALLATIONS: + $this->deleteInstallation($dbForConsole, $document, $project); + break; + case DELETE_TYPE_RULES: + $this->deleteRule($dbForConsole, $document, $project); break; default: if (\str_starts_with($document->getCollection(), 'database_')) { @@ -110,9 +124,7 @@ class Deletes extends Action if (!$document->isEmpty()) { $this->deleteAuditLogsByResource($getProjectDB, 'document/' . $document->getId(), $project); } - break; - case DELETE_TYPE_ABUSE: $this->deleteAbuseLogs($dbForConsole, $getProjectDB, $datetime); break; @@ -124,20 +136,14 @@ class Deletes extends Action case DELETE_TYPE_SESSIONS: $this->deleteExpiredSessions($dbForConsole, $getProjectDB); break; - - case DELETE_TYPE_CERTIFICATES: - $this->deleteCertificates($dbForConsole, $document); - break; - case DELETE_TYPE_USAGE: - $this->deleteUsageStats($dbForConsole, $getProjectDB, $hourlyUsageRetentionDatetime); + $this->deleteUsageStats($getProjectDB, $hourlyUsageRetentionDatetime); break; - case DELETE_TYPE_CACHE_BY_RESOURCE: - $this->deleteCacheByResource($dbForConsole, $getProjectDB, $resource); + $this->deleteCacheByResource($project, $getProjectDB, $resource); break; case DELETE_TYPE_CACHE_BY_TIMESTAMP: - $this->deleteCacheByDate($dbForConsole, $getProjectDB, $datetime); + $this->deleteCacheByDate($project, $getProjectDB, $datetime); break; case DELETE_TYPE_SCHEDULES: $this->deleteSchedules($dbForConsole, $getProjectDB, $datetime); @@ -154,7 +160,7 @@ class Deletes extends Action * @param string $datetime * @return void * @throws Authorization - * @throws \Throwable + * @throws Throwable */ protected function deleteSchedules(Database $dbForConsole, callable $getProjectDB, string $datetime): void { @@ -186,49 +192,27 @@ class Deletes extends Action } /** - * @param Database $dbForConsole + * @param Document $project * @param callable $getProjectDB * @param string $resource - * @throws Exception + * @throws Authorization */ - protected function deleteCacheByResource(Database $dbForConsole, callable $getProjectDB, string $resource): void + protected function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void { - $this->deleteCacheFiles($dbForConsole, $getProjectDB, [ - Query::equal('resource', [$resource]), - ]); - } + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + $document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]); - /** - * @throws Exception - */ - protected function deleteCacheByDate(Database $dbForConsole, callable $getProjectDB, string $datetime): void - { - $this->deleteCacheFiles($dbForConsole, $getProjectDB, [ - Query::lessThan('accessedAt', $datetime), - ]); - } - /** - * @param Database $dbForConsole - * @param callable $getProjectDB - * @param array $query - * @return void - * @throws Exception - */ - protected function deleteCacheFiles(Database $dbForConsole, callable $getProjectDB, array $query): void - { - $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($query, $getProjectDB) { - $projectId = $project->getId(); - $dbForProject = $getProjectDB($project); + if ($document) { $cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) ); - $this->deleteByGroup( - 'cache', - $query, + $this->deleteById( + $document, $dbForProject, - function (Document $document) use ($cache, $projectId) { + function ($document) use ($cache, $projectId) { $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); if ($cache->purge($document->getId())) { @@ -238,12 +222,49 @@ class Deletes extends Action } } ); - }); + } + } + + /** + * Document $project + * @param Document $project + * @param callable $getProjectDB + * @param string $datetime + * @return void + * @throws Exception + */ + protected function deleteCacheByDate(Document $project, callable $getProjectDB, string $datetime): void + { + $projectId = $project->getId(); + $dbForProject = $getProjectDB($project); + + $cache = new Cache( + new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) + ); + + $query = [ + Query::lessThan('accessedAt', $datetime), + ]; + + $this->deleteByGroup( + 'cache', + $query, + $dbForProject, + function (Document $document) use ($cache, $projectId) { + $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); + + if ($cache->purge($document->getId())) { + Console::success('Deleting cache file: ' . $path); + } else { + Console::error('Failed to delete cache file: ' . $path); + } + } + ); } /** * @param callable $getProjectDB - * @param Document $document database document + * @param Document $document * @param Document $project * @throws Exception */ @@ -269,19 +290,37 @@ class Deletes extends Action protected function deleteCollection(callable $getProjectDB, Document $document, Document $project): void { $collectionId = $document->getId(); + $collectionInternalId = $document->getInternalId(); $databaseId = $document->getAttribute('databaseId'); $databaseInternalId = $document->getAttribute('databaseInternalId'); + $dbForProject = $getProjectDB($project); + $relationships = \array_filter( + $document->getAttribute('attributes'), + fn ($attribute) => $attribute['type'] === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + if (!$relationship['twoWay']) { + continue; + } + $relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']); + $dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']); + $dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId()); + $dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId()); + } + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); + $this->deleteByGroup('attributes', [ - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]) + Query::equal('databaseInternalId', [$databaseInternalId]), + Query::equal('collectionInternalId', [$collectionInternalId]) ], $dbForProject); $this->deleteByGroup('indexes', [ - Query::equal('databaseId', [$databaseId]), - Query::equal('collectionId', [$collectionId]) + Query::equal('databaseInternalId', [$databaseInternalId]), + Query::equal('collectionInternalId', [$collectionInternalId]) ], $dbForProject); $this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId . '/collection/' . $collectionId, $project); @@ -313,31 +352,65 @@ class Deletes extends Action */ protected function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void { - $teamId = $document->getAttribute('teamId', ''); + $dbForProject = $getProjectDB($project); + $teamInternalId = $document->getInternalId(); // Delete Memberships - $this->deleteByGroup('memberships', [ - Query::equal('teamId', [$teamId]) - ], $getProjectDB($project)); + $this->deleteByGroup( + 'memberships', + [ + Query::equal('teamInternalId', [$teamInternalId]) + ], + $dbForProject, + function (Document $membership) use ($dbForProject) { + $userId = $membership->getAttribute('userId'); + $dbForProject->deleteCachedDocument('users', $userId); + } + ); + } + + /** + * @param Database $dbForConsole + * @param Document $document + * @return void + * @throws Authorization + * @throws \Utopia\Database\Exception + * @throws Conflict + * @throws Restricted + * @throws Structure + */ + protected function deleteProjectsByTeam(Database $dbForConsole, Document $document): void + { + + $projects = $dbForConsole->find('projects', [ + Query::equal('teamInternalId', [$document->getInternalId()]) + ]); + + foreach ($projects as $project) { + $this->deleteProject($dbForConsole, $project); + $dbForConsole->deleteDocument('projects', $project->getId()); + } } /** * @param Database $dbForConsole * @param callable $getProjectDB - * @param callable $deviceFiles - * @param callable $deviceFunctions - * @param callable $deviceBuilds - * @param callable $deviceCache - * @param Document $document project document - * @throws Authorization + * @param callable $getFilesDevice + * @param callable $getFunctionsDevice + * @param callable $getBuildsDevice + * @param callable $getCacheDevice + * @param Document $document + * @throws Authorization|\Utopia\Database\Exception + * @throws Exception */ - protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $deviceFiles, callable $deviceFunctions, callable $deviceBuilds, callable $deviceCache, Document $document): void + protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void { $projectId = $document->getId(); + $projectInternalId = $document->getInternalId(); - // Delete project domains and certificates + // Delete project certificates $domains = $dbForConsole->find('domains', [ - Query::equal('projectInternalId', [$document->getInternalId()]) + Query::equal('projectInternalId', [$projectInternalId]) ]); foreach ($domains as $domain) { @@ -359,6 +432,26 @@ class Deletes extends Action } } + // Delete Platforms + $this->deleteByGroup('platforms', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete Domains + $this->deleteByGroup('domains', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete Keys + $this->deleteByGroup('keys', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete Webhooks + $this->deleteByGroup('webhooks', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + // Delete metadata tables try { $dbForProject->deleteCollection('_metadata'); @@ -368,10 +461,10 @@ class Deletes extends Action } // Delete all storage directories - $uploads = $deviceFiles($projectId); - $functions = $deviceFunctions($projectId); - $builds = $deviceBuilds($projectId); - $cache = $deviceCache($projectId); + $uploads = $getFilesDevice($projectId); + $functions = $getFunctionsDevice($projectId); + $builds = $getBuildsDevice($projectId); + $cache = $getCacheDevice($projectId); $uploads->delete($uploads->getRoot(), true); $functions->delete($functions->getRoot(), true); @@ -379,6 +472,52 @@ class Deletes extends Action $cache->delete($cache->getRoot(), true); } + /** + * @param Database $dbForConsole + * @param Document $document certificates document + */ + protected function deleteCertificates(Database $dbForConsole, Document $document): void + { + // If domain has certificate generated + if (isset($document['certificateId'])) { + $domainUsingCertificate = $dbForConsole->findOne('domains', [ + Query::equal('certificateId', [$document['certificateId']]) + ]); + + if (!$domainUsingCertificate) { + $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); + if ($mainDomain === $document->getAttribute('domain')) { + $domainUsingCertificate = $mainDomain; + } + } + + // If certificate is still used by some domain, mark we can't delete. + // Current domain should not be found, because we only have copy. Original domain is already deleted from database. + if ($domainUsingCertificate) { + Console::warning("Skipping certificate deletion, because a domain is still using it."); + return; + } + } + + $domain = $document->getAttribute('domain'); + $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; + $checkTraversal = realpath($directory) === $directory; + + if ($domain && $checkTraversal && is_dir($directory)) { + // Delete certificate document, so Appwrite is aware of change + if (isset($document['certificateId'])) { + $dbForConsole->deleteDocument('certificates', $document['certificateId']); + } + + // Delete files, so Traefik is aware of change + array_map('unlink', glob($directory . '/*.*')); + rmdir($directory); + Console::info("Deleted certificate files for {$domain}"); + } else { + Console::info("No certificate files found for {$domain}"); + } + } + /** * @param callable $getProjectDB * @param Document $document user document @@ -388,18 +527,19 @@ class Deletes extends Action protected function deleteUser(callable $getProjectDB, Document $document, Document $project): void { $userId = $document->getId(); + $userInternalId = $document->getInternalId(); $dbForProject = $getProjectDB($project); // Delete all sessions of this user from the sessions table and update the sessions field of the user record $this->deleteByGroup('sessions', [ - Query::equal('userId', [$userId]) + Query::equal('userInternalId', [$userInternalId]) ], $dbForProject); $dbForProject->deleteCachedDocument('users', $userId); // Delete Memberships and decrement team membership counts $this->deleteByGroup('memberships', [ - Query::equal('userId', [$userId]) + Query::equal('userInternalId', [$userInternalId]) ], $dbForProject, function (Document $document) use ($dbForProject) { if ($document->getAttribute('confirm')) { // Count only confirmed members $teamId = $document->getAttribute('teamId'); @@ -417,7 +557,12 @@ class Deletes extends Action // Delete tokens $this->deleteByGroup('tokens', [ - Query::equal('userId', [$userId]) + Query::equal('userInternalId', [$userInternalId]) + ], $dbForProject); + + // Delete identities + $this->deleteByGroup('identities', [ + Query::equal('userInternalId', [$userInternalId]) ], $dbForProject); } @@ -442,7 +587,7 @@ class Deletes extends Action * @param Database $dbForConsole * @param callable $getProjectDB * @return void - * @throws Exception|\Throwable + * @throws Exception|Throwable */ protected function deleteExpiredSessions(Database $dbForConsole, callable $getProjectDB): void { @@ -541,36 +686,50 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $deviceFunctions - * @param callable $deviceBuilds - * @param Document $document function document + * @param callable $getFunctionsDevice + * @param callable $getBuildsDevice + * @param Document $document * @param Document $project * @throws Exception */ - protected function deleteFunction(callable $getProjectDB, callable $deviceFunctions, callable $deviceBuilds, Document $document, Document $project): void + protected function deleteFunction(Database $dbForConsole, callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $functionId = $document->getId(); + $functionInternalId = $document->getInternalId(); + + /** + * Delete rules + */ + Console::info("Deleting rules for function " . $functionId); + $this->deleteByGroup('rules', [ + Query::equal('resourceType', ['function']), + Query::equal('resourceInternalId', [$functionInternalId]), + Query::equal('projectInternalId', [$project->getInternalId()]) + ], $dbForConsole, function (Document $document) use ($project, $dbForConsole) { + $this->deleteRule($dbForConsole, $document, $project); + }); /** * Delete Variables */ Console::info("Deleting variables for function " . $functionId); $this->deleteByGroup('variables', [ - Query::equal('functionId', [$functionId]) + Query::equal('resourceType', ['function']), + Query::equal('resourceInternalId', [$functionInternalId]) ], $dbForProject); /** * Delete Deployments */ Console::info("Deleting deployments for function " . $functionId); - $storageFunctions = $deviceFunctions($projectId); - $deploymentIds = []; + $storageFunctions = $getFunctionsDevice($projectId); + $deploymentInternalIds = []; $this->deleteByGroup('deployments', [ - Query::equal('resourceId', [$functionId]) - ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) { - $deploymentIds[] = $document->getId(); + Query::equal('resourceInternalId', [$functionInternalId]) + ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentInternalIds) { + $deploymentInternalIds[] = $document->getInternalId(); if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); } else { @@ -582,15 +741,15 @@ class Deletes extends Action * Delete builds */ Console::info("Deleting builds for function " . $functionId); - $storageBuilds = $deviceBuilds($projectId); - foreach ($deploymentIds as $deploymentId) { + $storageBuilds = $getBuildsDevice($projectId); + foreach ($deploymentInternalIds as $deploymentInternalId) { $this->deleteByGroup('builds', [ - Query::equal('deploymentId', [$deploymentId]) - ], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) { - if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); + Query::equal('deploymentInternalId', [$deploymentInternalId]) + ], $dbForProject, function (Document $document) use ($storageBuilds) { + if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('path', '')); } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); + Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); } }); } @@ -600,31 +759,36 @@ class Deletes extends Action */ Console::info("Deleting executions for function " . $functionId); $this->deleteByGroup('executions', [ - Query::equal('functionId', [$functionId]) + Query::equal('functionInternalId', [$functionInternalId]) ], $dbForProject); - // TODO: Request executor to delete runtime + /** + * Request executor to delete all deployment containers + */ + Console::info("Requesting executor to delete all deployment containers for function " . $functionId); + $this->deleteRuntimes($document, $project); } /** * @param callable $getProjectDB - * @param callable $deviceFunctions - * @param callable $deviceBuilds - * @param Document $document deployment document + * @param callable $getFunctionsDevice + * @param callable $getBuildsDevice + * @param Document $document * @param Document $project * @throws Exception */ - protected function deleteDeployment(callable $getProjectDB, callable $deviceFunctions, callable $deviceBuilds, Document $document, Document $project): void + protected function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $deploymentId = $document->getId(); + $deploymentInternalId = $document->getInternalId(); /** * Delete deployment files */ Console::info("Deleting deployment files for deployment " . $deploymentId); - $storageFunctions = $deviceFunctions($projectId); + $storageFunctions = $getFunctionsDevice($projectId); if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); } else { @@ -635,21 +799,27 @@ class Deletes extends Action * Delete builds */ Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $deviceBuilds($projectId); + $storageBuilds = $getBuildsDevice($projectId); $this->deleteByGroup('builds', [ - Query::equal('deploymentId', [$deploymentId]) + Query::equal('deploymentInternalId', [$deploymentInternalId]) ], $dbForProject, function (Document $document) use ($storageBuilds) { - if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('outputPath', '')); + if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { + Console::success('Deleted build files: ' . $document->getAttribute('path', '')); } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('outputPath', '')); + Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); } }); - // TODO: Request executor to delete runtime + + /** + * Request executor to delete all deployment containers + */ + Console::info("Requesting executor to delete deployment container for deployment " . $deploymentId); + $this->deleteRuntimes($document, $project); } + /** * @param Document $document to be deleted * @param Database $database to delete it from @@ -785,42 +955,17 @@ class Deletes extends Action /** * @param Database $dbForConsole - * @param Document $document certificates document - * @throws Authorization + * @param Document $document rule document + * @param Document $project project document */ - protected function deleteCertificates(Database $dbForConsole, Document $document): void + protected function deleteRule(Database $dbForConsole, Document $document, Document $project): void { - // If domain has certificate generated - if (isset($document['certificateId'])) { - $domainUsingCertificate = $dbForConsole->findOne('domains', [ - Query::equal('certificateId', [$document['certificateId']]) - ]); - - if (!$domainUsingCertificate) { - $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); - if ($mainDomain === $document->getAttribute('domain')) { - $domainUsingCertificate = $mainDomain; - } - } - - // If certificate is still used by some domain, mark we can't delete. - // Current domain should not be found, because we only have copy. Original domain is already deleted from database. - if ($domainUsingCertificate) { - Console::warning("Skipping certificate deletion, because a domain is still using it."); - return; - } - } $domain = $document->getAttribute('domain'); $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; $checkTraversal = realpath($directory) === $directory; - if ($domain && $checkTraversal && is_dir($directory)) { - // Delete certificate document, so Appwrite is aware of change - if (isset($document['certificateId'])) { - $dbForConsole->deleteDocument('certificates', $document['certificateId']); - } - + if ($checkTraversal && is_dir($directory)) { // Delete files, so Traefik is aware of change array_map('unlink', glob($directory . '/*.*')); rmdir($directory); @@ -828,24 +973,109 @@ class Deletes extends Action } else { Console::info("No certificate files found for {$domain}"); } + + // Delete certificate document, so Appwrite is aware of change + if (isset($document['certificateId'])) { + $dbForConsole->deleteDocument('certificates', $document['certificateId']); + } } /** * @param callable $getProjectDB - * @param callable $deviceFiles + * @param callable $getFilesDevice * @param Document $document * @param Document $project * @return void */ - protected function deleteBucket(callable $getProjectDB, callable $deviceFiles, Document $document, Document $project): void + protected function deleteBucket(callable $getProjectDB, callable $getFilesDevice, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); - $device = $deviceFiles($projectId); + $device = $getFilesDevice($projectId); $device->deletePath($document->getId()); } + + /** + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param Document $document + * @param Document $project + * @return void + */ + protected function deleteInstallation(Database $dbForConsole, callable $getProjectDB, Document $document, Document $project) + { + $dbForProject = $getProjectDB($project); + + $this->listByGroup('functions', [ + Query::equal('installationInternalId', [$document->getInternalId()]) + ], $dbForProject, function ($function) use ($dbForProject, $dbForConsole) { + $dbForConsole->deleteDocument('repositories', $function->getAttribute('repositoryId')); + + $function = $function + ->setAttribute('installationId', '') + ->setAttribute('installationInternalId', '') + ->setAttribute('providerRepositoryId', '') + ->setAttribute('providerBranch', '') + ->setAttribute('providerSilentMode', false) + ->setAttribute('providerRootDirectory', '') + ->setAttribute('repositoryId', '') + ->setAttribute('repositoryInternalId', ''); + $dbForProject->updateDocument('functions', $function->getId(), $function); + }); + } + + /** + * @param callable $getProjectDB + * @param ?Document $function + * @param Document $project + * @throws Exception + */ + protected function deleteRuntimes(callable $getProjectDB, ?Document $function, Document $project) + { + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + + $deleteByFunction = function (Document $function) use ($getProjectDB, $project, $executor) { + $this->listByGroup( + 'deployments', + [ + Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceType', ['functions']), + ], + $getProjectDB($project), + function (Document $deployment) use ($project, $executor) { + $deploymentId = $deployment->getId(); + + try { + $executor->deleteRuntime($project->getId(), $deploymentId); + Console::info("Runtime for deployment {$deploymentId} deleted."); + } catch (Throwable $th) { + Console::warning("Runtime for deployment {$deploymentId} skipped:"); + Console::error('[Error] Type: ' . get_class($th)); + Console::error('[Error] Message: ' . $th->getMessage()); + Console::error('[Error] File: ' . $th->getFile()); + Console::error('[Error] Line: ' . $th->getLine()); + } + } + ); + }; + + if ($function !== null) { + // Delete function runtimes + $deleteByFunction($function); + } else { + // Delete all project runtimes + $this->listByGroup( + 'functions', + [], + $getProjectDB($project), + function (Document $function) use ($deleteByFunction) { + $deleteByFunction($function); + } + ); + } + } } From b02d51c7945e0539eeb57714acae89b1b57579cb Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 1 Oct 2023 20:39:26 +0300 Subject: [PATCH 047/144] migrations worker --- .gitmodules | 2 +- app/controllers/api/account.php | 2 +- app/controllers/api/databases.php | 22 +- app/controllers/api/functions.php | 11 +- app/controllers/api/migrations.php | 63 ++-- app/controllers/api/teams.php | 7 +- app/controllers/shared/api.php | 7 +- app/init.php | 6 +- app/worker.php | 16 +- bin/worker-migrations | 9 +- src/Appwrite/Event/Migration.php | 26 +- src/Appwrite/Platform/Services/Workers.php | 2 + src/Appwrite/Platform/Workers/Audits.php | 9 +- src/Appwrite/Platform/Workers/Builds.php | 51 ++- .../Platform/Workers/Certificates.php | 30 +- src/Appwrite/Platform/Workers/Databases.php | 9 +- src/Appwrite/Platform/Workers/Deletes.php | 39 ++- src/Appwrite/Platform/Workers/Functions.php | 34 +- src/Appwrite/Platform/Workers/Mails.php | 76 ++-- src/Appwrite/Platform/Workers/Messaging.php | 19 +- src/Appwrite/Platform/Workers/Migrations.php | 330 ++++++++++++++++++ src/Appwrite/Platform/Workers/Usage.php | 22 +- src/Appwrite/Platform/Workers/UsageHook.php | 7 +- src/Appwrite/Platform/Workers/Webhooks.php | 21 +- 24 files changed, 683 insertions(+), 137 deletions(-) create mode 100644 src/Appwrite/Platform/Workers/Migrations.php diff --git a/.gitmodules b/.gitmodules index 33e1bf0f0f..09a9253edd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.1.1 + branch = feat-usage-1.4 diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ed4cc6c4a3..7c7230bbf6 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -76,7 +76,7 @@ App::post('/v1/account') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) { $email = \strtolower($email); if ('console' === $project->getId()) { diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 786cabc303..596ea6171b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -22,6 +22,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; +use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; @@ -57,13 +58,26 @@ use Utopia\Validator\URL; use Utopia\Validator\WhiteList; /** - * Create attribute of varying type - * + * * Create attribute of varying type * + * @param string $databaseId + * @param string $collectionId + * @param Document $attribute + * @param Response $response + * @param Database $dbForProject + * @param EventDatabase $queueForDatabase + * @param Event $queueForEvents * @return Document Newly created attribute document + * @throws AuthorizationException * @throws Exception + * @throws LimitException + * @throws RestrictedException + * @throws StructureException + * @throws \Utopia\Database\Exception + * @throws Conflict + * @throws \Utopia\Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $events): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -1529,7 +1543,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $database, $events); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 9d598b58b7..8fcee70530 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -285,7 +285,12 @@ App::post('/v1/functions') /** Trigger Webhook */ $ruleModel = new Rule(); - $ruleCreate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); + $ruleCreate = + $queueForEvents + ->setClass(Event::WEBHOOK_CLASS_NAME) + ->setQueue(Event::WEBHOOK_QUEUE_NAME) + ; + $ruleCreate ->setProject($project) ->setEvent('rules.[ruleId].create') @@ -999,7 +1004,9 @@ App::post('/v1/functions/:functionId/deployments') ->inject('deviceLocal') ->inject('dbForConsole') ->inject('queueForBuilds') - ->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole, Build $queueForBuilds) { + ->action(function (string $functionId, string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole, Build $queueForBuilds) { + + $activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN); $function = $dbForProject->getDocument('functions', $functionId); diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 83888fff2c..d51c6adbf7 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -51,8 +51,9 @@ App::post('/v1/migrations/appwrite') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') - ->action(function (array $resources, string $endpoint, string $projectId, string $apiKey, Response $response, Database $dbForProject, Document $project, Document $user, Event $events) { + ->inject('queueForEvents') + ->inject('queueForMigrations') + ->action(function (array $resources, string $endpoint, string $projectId, string $apiKey, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) { $migration = $dbForProject->createDocument('migrations', new Document([ '$id' => ID::unique(), 'status' => 'pending', @@ -69,11 +70,10 @@ App::post('/v1/migrations/appwrite') 'errors' => [], ])); - $events->setParam('migrationId', $migration->getId()); + $queueForEvents->setParam('migrationId', $migration->getId()); // Trigger Transfer - $event = new Migration(); - $event + $queueForMigrations ->setMigration($migration) ->setProject($project) ->setUser($user) @@ -104,9 +104,10 @@ App::post('/v1/migrations/firebase/oauth') ->inject('dbForConsole') ->inject('project') ->inject('user') - ->inject('events') + ->inject('queueForEvents') + ->inject('queueForMigrations') ->inject('request') - ->action(function (array $resources, string $projectId, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $events, Request $request) { + ->action(function (array $resources, string $projectId, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations, Request $request) { $firebase = new OAuth2Firebase( App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), @@ -171,11 +172,10 @@ App::post('/v1/migrations/firebase/oauth') 'errors' => [] ])); - $events->setParam('migrationId', $migration->getId()); + $queueForEvents->setParam('migrationId', $migration->getId()); // Trigger Transfer - $event = new Migration(); - $event + $queueForMigrations ->setMigration($migration) ->setProject($project) ->setUser($user) @@ -205,8 +205,9 @@ App::post('/v1/migrations/firebase') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') - ->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, Document $user, Event $events) { + ->inject('queueForEvents') + ->inject('queueForMigrations') + ->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) { $migration = $dbForProject->createDocument('migrations', new Document([ '$id' => ID::unique(), 'status' => 'pending', @@ -221,11 +222,10 @@ App::post('/v1/migrations/firebase') 'errors' => [], ])); - $events->setParam('migrationId', $migration->getId()); + $queueForEvents->setParam('migrationId', $migration->getId()); // Trigger Transfer - $event = new Migration(); - $event + $queueForMigrations ->setMigration($migration) ->setProject($project) ->setUser($user) @@ -260,8 +260,9 @@ App::post('/v1/migrations/supabase') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') - ->action(function (array $resources, string $endpoint, string $apiKey, string $databaseHost, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, Document $user, Event $events) { + ->inject('queueForEvents') + ->inject('queueForMigrations') + ->action(function (array $resources, string $endpoint, string $apiKey, string $databaseHost, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) { $migration = $dbForProject->createDocument('migrations', new Document([ '$id' => ID::unique(), 'status' => 'pending', @@ -281,11 +282,10 @@ App::post('/v1/migrations/supabase') 'errors' => [], ])); - $events->setParam('migrationId', $migration->getId()); + $queueForEvents->setParam('migrationId', $migration->getId()); // Trigger Transfer - $event = new Migration(); - $event + $queueForMigrations ->setMigration($migration) ->setProject($project) ->setUser($user) @@ -321,8 +321,9 @@ App::post('/v1/migrations/nhost') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') - ->action(function (array $resources, string $subdomain, string $region, string $adminSecret, string $database, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, Document $user, Event $events) { + ->inject('queueForEvents') + ->inject('queueForMigrations') + ->action(function (array $resources, string $subdomain, string $region, string $adminSecret, string $database, string $username, string $password, int $port, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) { $migration = $dbForProject->createDocument('migrations', new Document([ '$id' => ID::unique(), 'status' => 'pending', @@ -343,11 +344,10 @@ App::post('/v1/migrations/nhost') 'errors' => [], ])); - $events->setParam('migrationId', $migration->getId()); + $queueForEvents->setParam('migrationId', $migration->getId()); // Trigger Transfer - $event = new Migration(); - $event + $queueForMigrations ->setMigration($migration) ->setProject($project) ->setUser($user) @@ -931,8 +931,8 @@ App::patch('/v1/migrations/:migrationId') ->inject('dbForProject') ->inject('project') ->inject('user') - ->inject('events') - ->action(function (string $migrationId, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventInstance) { + ->inject('queueForMigrations') + ->action(function (string $migrationId, Response $response, Database $dbForProject, Document $project, Document $user, Migration $queueForMigrations) { $migration = $dbForProject->getDocument('migrations', $migrationId); if ($migration->isEmpty()) { @@ -948,8 +948,7 @@ App::patch('/v1/migrations/:migrationId') ->setAttribute('dateUpdated', \time()); // Trigger Migration - $event = new Migration(); - $event + $queueForMigrations ->setMigration($migration) ->setProject($project) ->setUser($user) @@ -974,8 +973,8 @@ App::delete('/v1/migrations/:migrationId') ->param('migrationId', '', new UID(), 'Migration ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $migrationId, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $migrationId, Response $response, Database $dbForProject, Event $queueForEvents) { $migration = $dbForProject->getDocument('migrations', $migrationId); if ($migration->isEmpty()) { @@ -986,7 +985,7 @@ App::delete('/v1/migrations/:migrationId') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove migration from DB'); } - $events->setParam('migrationId', $migration->getId()); + $queueForEvents->setParam('migrationId', $migration->getId()); $response->noContent(); }); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 2fec0c24fc..f52121743a 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -388,7 +388,9 @@ App::post('/v1/teams/:teamId/memberships') ->inject('queueForMails') ->inject('queueForMessaging') ->inject('queueForEvents') - ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, EventPhone $queueForMessaging, Event $queueForEvents) { + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, EventPhone $queueForMessaging, Event $queueForEvents) { + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if (empty($url)) { if (!$isAPIKey && !$isPrivilegedUser) { @@ -574,7 +576,7 @@ App::post('/v1/teams/:teamId/memberships') $replyTo = $smtp['replyTo']; } - $mails + $queueForMails ->setSmtpHost($smtp['host'] ?? '') ->setSmtpPort($smtp['port'] ?? '') ->setSmtpUsername($smtp['username'] ?? '') @@ -1094,7 +1096,6 @@ App::get('/v1/teams/:teamId/logs') $audit = new Audit($dbForProject); $resource = 'team/' . $team->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); - $output = []; foreach ($logs as $i => &$log) { diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 57319a6985..4afadedbd0 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -249,8 +249,8 @@ App::init() $queueForDatabase->setProject($project); $dbForProject - ->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) - ->on(Database::EVENT_DOCUMENT_DELETE, fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) + ->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) + ->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) ; $useCache = $route->getLabel('cache', false); @@ -427,7 +427,6 @@ App::shutdown() $responsePayload = $response->getPayload(); if (!empty($queueForEvents->getEvent())) { - if (empty($queueForEvents->getPayload())) { $queueForEvents->setPayload($responsePayload); } @@ -497,7 +496,7 @@ App::shutdown() } if (!$user->isEmpty()) { - $audits->setUser($user); + $queueForAudits->setUser($user); } if (!empty($queueForAudits->getResource()) && !empty($queueForAudits->getUser()->getId())) { diff --git a/app/init.php b/app/init.php index c156d00369..f22fe089a0 100644 --- a/app/init.php +++ b/app/init.php @@ -18,6 +18,7 @@ ini_set('display_startup_errors', 1); ini_set('default_socket_timeout', -1); error_reporting(E_ALL); +use Appwrite\Event\Migration; use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\Auth\Auth; @@ -53,6 +54,7 @@ use Utopia\Messaging\Adapters\SMS\Telesign; use Utopia\Messaging\Adapters\SMS\TextMagic; use Utopia\Messaging\Adapters\SMS\Twilio; use Utopia\Messaging\Adapters\SMS\Vonage; +use Utopia\Queue\Server; use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Backblaze; @@ -896,7 +898,9 @@ App::setResource('queueForUsage', function (Connection $queue) { App::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); - +App::setResource('queueForMigrations', function (Connection $queue) { + return new Migration($queue); +}, ['queue']); App::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), diff --git a/app/worker.php b/app/worker.php index ed499eca9b..be4fd2c665 100644 --- a/app/worker.php +++ b/app/worker.php @@ -10,6 +10,7 @@ use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\Mail; +use Appwrite\Event\Migration; use Appwrite\Event\Phone; use Appwrite\Event\Usage; use Appwrite\Platform\Appwrite; @@ -76,7 +77,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases): Database { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -154,6 +155,9 @@ Server::setResource('queueForCertificates', function (Connection $queue) { Server::setResource('queueForUsage', function (Connection $queue) { return new Usage($queue); }, ['queue']); +Server::setResource('queueForMigrations', function (Connection $queue) { + return new Migration($queue); +}, ['queue']); Server::setResource('logger', function (Registry $register) { return $register->get('logger'); }, ['register']); @@ -169,7 +173,7 @@ Server::setResource('log', fn() => new Log()); * @param string $projectId of the project * @return Device */ -Server::setResource('deviceFunctions', function () { +Server::setResource('getFunctionsDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); }; @@ -180,7 +184,7 @@ Server::setResource('deviceFunctions', function () { * @param string $projectId of the project * @return Device */ -Server::setResource('deviceFiles', function () { +Server::setResource('getFilesDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); }; @@ -191,7 +195,7 @@ Server::setResource('deviceFiles', function () { * @param string $projectId of the project * @return Device */ -Server::setResource('deviceBuilds', function () { +Server::setResource('getBuildsDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); }; @@ -202,7 +206,7 @@ Server::setResource('deviceBuilds', function () { * @param string $projectId of the project * @return Device */ -Server::setResource('deviceCache', function () { +Server::setResource('getCacheDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_CACHE . '/app-' . $projectId); }; @@ -286,4 +290,4 @@ try { }); } -$worker->start(); \ No newline at end of file +$worker->start(); diff --git a/bin/worker-migrations b/bin/worker-migrations index 54e57001b0..32d4aef468 100644 --- a/bin/worker-migrations +++ b/bin/worker-migrations @@ -1,10 +1,3 @@ #!/bin/sh -if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ] -then - REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -else - REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" -fi - -INTERVAL=0.1 QUEUE='v1-migrations' APP_INCLUDE='/usr/src/code/app/workers/migrations.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +php /usr/src/code/app/worker.php migrations $@ \ No newline at end of file diff --git a/src/Appwrite/Event/Migration.php b/src/Appwrite/Event/Migration.php index 4d53f16796..09fb6a8699 100644 --- a/src/Appwrite/Event/Migration.php +++ b/src/Appwrite/Event/Migration.php @@ -6,15 +6,21 @@ use DateTime; use Resque; use ResqueScheduler; use Utopia\Database\Document; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class Migration extends Event { protected string $type = ''; protected ?Document $migration = null; - public function __construct() + public function __construct(protected Connection $connection) { - parent::__construct(Event::MIGRATIONS_QUEUE_NAME, Event::MIGRATIONS_CLASS_NAME); + parent::__construct($connection); + + $this + ->setQueue(Event::MIGRATIONS_QUEUE_NAME) + ->setClass(Event::MIGRATIONS_CLASS_NAME); } /** @@ -72,7 +78,10 @@ class Migration extends Event */ public function trigger(): string|bool { - return Resque::enqueue($this->queue, $this->class, [ + + $client = new Client($this->queue, $this->connection); + + return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, 'migration' => $this->migration @@ -89,10 +98,11 @@ class Migration extends Event */ public function schedule(DateTime|int $at): void { - ResqueScheduler::enqueueAt($at, $this->queue, $this->class, [ - 'project' => $this->project, - 'user' => $this->user, - 'migration' => $this->migration - ]); + return; +// ResqueScheduler::enqueueAt($at, $this->queue, $this->class, [ +// 'project' => $this->project, +// 'user' => $this->user, +// 'migration' => $this->migration +// ]); } } diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index e93b74ce98..6ec841bf23 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -14,6 +14,7 @@ use Appwrite\Platform\Workers\Builds; use Appwrite\Platform\Workers\Deletes; use Appwrite\Platform\Workers\Usage; use Appwrite\Platform\Workers\UsageHook; +use Appwrite\Platform\Workers\Migrations; class Workers extends Service { @@ -32,6 +33,7 @@ class Workers extends Service ->addAction(Deletes::getName(), new Deletes()) ->addAction(UsageHook::getName(), new UsageHook()) ->addAction(Usage::getName(), new Usage()) + ->addAction(Usage::getName(), new Migrations()) ; } diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index ac6388855e..038cad13ef 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -7,6 +7,8 @@ use Throwable; use Utopia\Audit\Audit; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Structure; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -31,8 +33,13 @@ class Audits extends Action /** - * @throws Exception + * @param Message $message + * @param Database $dbForProject + * @return void * @throws Throwable + * @throws \Utopia\Database\Exception + * @throws Authorization + * @throws Structure */ public function action(Message $message, Database $dbForProject): void { diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 84a431f939..ae508f1452 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -18,12 +18,13 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Exception\Conflict; +use Utopia\Database\Exception\Restricted; use Utopia\Database\Validator\Authorization; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; use Utopia\VCS\Adapter\Git\GitHub; @@ -54,7 +55,17 @@ class Builds extends Action } /** - * @throws Exception|\Throwable + * @param Message $message + * @param Database $dbForConsole + * @param Event $queueForEvents + * @param Func $queueForFunctions + * @param Usage $queueForUsage + * @param Cache $cache + * @param callable $getProjectDB + * @param callable $deviceFunctions + * @return void + * @throws \Utopia\Database\Exception + * @throws Exception */ public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $deviceFunctions): void { @@ -84,11 +95,21 @@ class Builds extends Action } /** - * @throws Authorization - * @throws \Throwable - * @throws Structure + * @param callable $deviceFunctions + * @param Func $queueForFunctions + * @param Event $queueForEvents + * @param Usage $queueForUsage + * @param Database $dbForConsole + * @param callable $getProjectDB + * @param GitHub $github + * @param Document $project + * @param Document $function + * @param Document $deployment + * @param Document $template + * @return void + * @throws \Utopia\Database\Exception */ - protected function buildDeployment(callable $deviceFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template) + protected function buildDeployment(callable $deviceFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); @@ -518,6 +539,24 @@ class Builds extends Action } } + /** + * @param string $status + * @param GitHub $github + * @param string $providerCommitHash + * @param string $owner + * @param string $repositoryName + * @param Document $project + * @param Document $function + * @param string $deploymentId + * @param Database $dbForProject + * @param Database $dbForConsole + * @return void + * @throws Structure + * @throws \Utopia\Database\Exception + * @throws Authorization + * @throws Conflict + * @throws Restricted + */ protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole): void { if ($function->getAttribute('providerSilentMode', false) === true) { diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 4c9eb0bbaf..00b008e72d 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -16,6 +16,9 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Conflict; +use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Domains\Domain; @@ -46,7 +49,14 @@ class Certificates extends Action } /** - * @throws Exception|Throwable + * @param Message $message + * @param Database $dbForConsole + * @param Mail $queueForMails + * @param Event $queueForEvents + * @param Func $queueForFunctions + * @return void + * @throws Throwable + * @throws \Utopia\Database\Exception */ public function action(Message $message, Database $dbForConsole, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions): void { @@ -64,7 +74,15 @@ class Certificates extends Action } /** - * @throws Exception|Throwable + * @param Domain $domain + * @param Database $dbForConsole + * @param Mail $queueForMails + * @param Event $queueForEvents + * @param Func $queueForFunctions + * @param bool $skipRenewCheck + * @return void + * @throws Throwable + * @throws \Utopia\Database\Exception */ private function execute(Domain $domain, Database $dbForConsole, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, bool $skipRenewCheck = false): void { @@ -176,9 +194,15 @@ class Certificates extends Action * * @param string $domain Domain name that certificate is for * @param Document $certificate Certificate document that we need to save + * @param bool $success * @param Database $dbForConsole Database connection for console + * @param Event $queueForEvents + * @param Func $queueForFunctions * @return void - * @throws Exception|Throwable + * @throws \Utopia\Database\Exception + * @throws Authorization + * @throws Conflict + * @throws Structure */ private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions): void { diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 79df4b3409..9e9c0126ad 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -39,6 +39,7 @@ class Databases extends Action * @param Message $message * @param Database $dbForConsole * @param Database $dbForProject + * @return void * @throws Exception */ public function action(Message $message, Database $dbForConsole, Database $dbForProject): void @@ -79,6 +80,7 @@ class Databases extends Action * @param Document $project * @param Database $dbForConsole * @param Database $dbForProject + * @return void * @throws Authorization * @throws Conflict * @throws Exception @@ -206,6 +208,7 @@ class Databases extends Action * @param Document $project * @param Database $dbForConsole * @param Database $dbForProject + * @return void * @throws Authorization * @throws Conflict * @throws Exception @@ -369,12 +372,13 @@ class Databases extends Action * @param Document $project * @param Database $dbForConsole * @param Database $dbForProject + * @return void * @throws Authorization * @throws Conflict * @throws Structure * @throws DatabaseException */ - private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject) + private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void { $projectId = $project->getId(); @@ -439,12 +443,13 @@ class Databases extends Action * @param Document $project * @param Database $dbForConsole * @param Database $dbForProject + * @return void * @throws Authorization * @throws Conflict * @throws Structure * @throws DatabaseException */ - private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject) + private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void { $projectId = $project->getId(); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index ce00d9c51d..a78e56bed0 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -137,7 +137,7 @@ class Deletes extends Action $this->deleteExpiredSessions($dbForConsole, $getProjectDB); break; case DELETE_TYPE_USAGE: - $this->deleteUsageStats($getProjectDB, $hourlyUsageRetentionDatetime); + $this->deleteUsageStats($dbForConsole, $getProjectDB, $hourlyUsageRetentionDatetime); break; case DELETE_TYPE_CACHE_BY_RESOURCE: $this->deleteCacheByResource($project, $getProjectDB, $resource); @@ -195,6 +195,7 @@ class Deletes extends Action * @param Document $project * @param callable $getProjectDB * @param string $resource + * @return void * @throws Authorization */ protected function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void @@ -266,6 +267,7 @@ class Deletes extends Action * @param callable $getProjectDB * @param Document $document * @param Document $project + * @return void * @throws Exception */ protected function deleteDatabase(callable $getProjectDB, Document $document, Document $project): void @@ -285,6 +287,7 @@ class Deletes extends Action * @param callable $getProjectDB * @param Document $document teams document * @param Document $project + * @return void * @throws Exception */ protected function deleteCollection(callable $getProjectDB, Document $document, Document $project): void @@ -330,9 +333,10 @@ class Deletes extends Action * @param Database $dbForConsole * @param callable $getProjectDB * @param string $hourlyUsageRetentionDatetime + * @return void * @throws Exception */ - protected function deleteUsageStats(Database $dbForConsole, callable $getProjectDB, string $hourlyUsageRetentionDatetime) + protected function deleteUsageStats(Database $dbForConsole, callable $getProjectDB, string $hourlyUsageRetentionDatetime): void { $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $hourlyUsageRetentionDatetime) { $dbForProject = $getProjectDB($project); @@ -348,6 +352,7 @@ class Deletes extends Action * @param callable $getProjectDB * @param Document $document teams document * @param Document $project + * @return void * @throws Exception */ protected function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void @@ -400,8 +405,10 @@ class Deletes extends Action * @param callable $getBuildsDevice * @param callable $getCacheDevice * @param Document $document - * @throws Authorization|\Utopia\Database\Exception + * @return void * @throws Exception + * @throws Authorization + * @throws \Utopia\Database\Exception */ protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void { @@ -475,6 +482,8 @@ class Deletes extends Action /** * @param Database $dbForConsole * @param Document $document certificates document + * @return void + * @throws Exception */ protected function deleteCertificates(Database $dbForConsole, Document $document): void { @@ -522,6 +531,7 @@ class Deletes extends Action * @param callable $getProjectDB * @param Document $document user document * @param Document $project + * @return void * @throws Exception */ protected function deleteUser(callable $getProjectDB, Document $document, Document $project): void @@ -570,6 +580,7 @@ class Deletes extends Action * @param database $dbForConsole * @param callable $getProjectDB * @param string $datetime + * @return void * @throws Exception */ protected function deleteExecutionLogs(database $dbForConsole, callable $getProjectDB, string $datetime): void @@ -609,6 +620,7 @@ class Deletes extends Action * @param Database $dbForConsole * @param callable $getProjectDB * @param string $datetime + * @return void * @throws Exception */ protected function deleteRealtimeUsage(Database $dbForConsole, callable $getProjectDB, string $datetime): void @@ -626,6 +638,7 @@ class Deletes extends Action * @param Database $dbForConsole * @param callable $getProjectDB * @param string $datetime + * @return void * @throws Exception */ protected function deleteAbuseLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void @@ -650,6 +663,7 @@ class Deletes extends Action * @param Database $dbForConsole * @param callable $getProjectDB * @param string $datetime + * @return void * @throws Exception */ protected function deleteAuditLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void @@ -673,6 +687,7 @@ class Deletes extends Action * @param callable $getProjectDB * @param string $resource * @param Document $project + * @return void * @throws Exception */ protected function deleteAuditLogsByResource(callable $getProjectDB, string $resource, Document $project): void @@ -688,8 +703,9 @@ class Deletes extends Action * @param callable $getProjectDB * @param callable $getFunctionsDevice * @param callable $getBuildsDevice - * @param Document $document + * @param Document $document function document * @param Document $project + * @return void * @throws Exception */ protected function deleteFunction(Database $dbForConsole, callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void @@ -775,6 +791,7 @@ class Deletes extends Action * @param callable $getBuildsDevice * @param Document $document * @param Document $project + * @return void * @throws Exception */ protected function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void @@ -815,7 +832,7 @@ class Deletes extends Action * Request executor to delete all deployment containers */ Console::info("Requesting executor to delete deployment container for deployment " . $deploymentId); - $this->deleteRuntimes($document, $project); + $this->deleteRuntimes($getProjectDB, $document, $project); } @@ -825,7 +842,6 @@ class Deletes extends Action * @param Database $database to delete it from * @param callable|null $callback to perform after document is deleted * @return bool - * @throws Authorization */ protected function deleteById(Document $document, Database $database, callable $callback = null): bool { @@ -854,9 +870,7 @@ class Deletes extends Action $count = 0; $chunk = 0; $limit = 50; - $projects = []; $sum = $limit; - $executionStart = \microtime(true); while ($sum === $limit) { @@ -883,6 +897,7 @@ class Deletes extends Action * @param array $queries * @param Database $database * @param callable|null $callback + * @return void * @throws Exception */ protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void @@ -920,6 +935,7 @@ class Deletes extends Action * @param Query[] $queries * @param Database $database * @param callable|null $callback + * @return void * @throws Exception */ protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void @@ -957,6 +973,7 @@ class Deletes extends Action * @param Database $dbForConsole * @param Document $document rule document * @param Document $project project document + * @return void */ protected function deleteRule(Database $dbForConsole, Document $document, Document $project): void { @@ -1005,8 +1022,9 @@ class Deletes extends Action * @param Document $document * @param Document $project * @return void + * @throws Exception */ - protected function deleteInstallation(Database $dbForConsole, callable $getProjectDB, Document $document, Document $project) + protected function deleteInstallation(Database $dbForConsole, callable $getProjectDB, Document $document, Document $project): void { $dbForProject = $getProjectDB($project); @@ -1032,9 +1050,10 @@ class Deletes extends Action * @param callable $getProjectDB * @param ?Document $function * @param Document $project + * @return void * @throws Exception */ - protected function deleteRuntimes(callable $getProjectDB, ?Document $function, Document $project) + protected function deleteRuntimes(callable $getProjectDB, ?Document $function, Document $project): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 695d1c7486..328d3e41bf 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -49,7 +49,15 @@ class Functions extends Action } /** - * @throws Exception|Throwable + * @param Message $message + * @param Database $dbForProject + * @param Func $queueForFunctions + * @param Event $queueForEvents + * @param Usage $queueForUsage + * @param Log $log + * @return void + * @throws Exception + * @throws Throwable */ public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log): void { @@ -84,7 +92,6 @@ class Functions extends Action $limit = 30; $sum = 30; $offset = 0; - $functions = []; /** @var Document[] $functions */ while ($sum >= $limit) { $functions = $dbForProject->find('functions', [ @@ -184,9 +191,28 @@ class Functions extends Action } /** + * @param Log $log + * @param Database $dbForProject + * @param Func $queueForFunctions + * @param Usage $queueForUsage + * @param Event $queueForEvents + * @param Document $project + * @param Document $function + * @param string $trigger + * @param string $path + * @param string $method + * @param array $headers + * @param string|null $data + * @param Document|null $user + * @param string|null $jwt + * @param string|null $event + * @param string|null $eventData + * @param string|null $executionId + * @return void * @throws Authorization - * @throws Throwable * @throws Structure + * @throws \Utopia\Database\Exception + * @throws \Utopia\Database\Exception\Conflict */ private function execute( Log $log, @@ -209,7 +235,6 @@ class Functions extends Action ): void { $user ??= new Document(); $functionId = $function->getId(); - $functionInternalId = $function->getInternalId(); $deploymentId = $function->getAttribute('deployment', ''); $log->addTag('functionId', $functionId); @@ -217,7 +242,6 @@ class Functions extends Action /** Check if deployment exists */ $deployment = $dbForProject->getDocument('deployments', $deploymentId); - $deploymentInternalId = $deployment->getInternalId(); if ($deployment->getAttribute('resourceId') !== $functionId) { throw new Exception('Deployment not found. Create deployment before trying to execute a function'); diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index d66650f186..9db6594a1b 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -7,10 +7,9 @@ use Exception; use PHPMailer\PHPMailer\PHPMailer; use Utopia\App; use Utopia\CLI\Console; -use Utopia\Database\Document; -use Utopia\Locale\Locale; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Registry\Registry; class Mails extends Action { @@ -32,10 +31,13 @@ class Mails extends Action } /** + * @param Message $message + * @param Registry $register * @throws \PHPMailer\PHPMailer\Exception + * @return void * @throws Exception */ - public function action(Message $message, $register): void + public function action(Message $message, Registry $register): void { $payload = $message->getPayload() ?? []; @@ -44,29 +46,30 @@ class Mails extends Action throw new Exception('Missing payload'); } - if (empty(App::getEnv('_APP_SMTP_HOST'))) { - Console::info('Skipped mail processing. No SMTP server hostname has been set.'); + $smtp = $payload['smtp']; + + if (empty($smtp) && empty(App::getEnv('_APP_SMTP_HOST'))) { + Console::info('Skipped mail processing. No SMTP configuration has been set.'); return; } $recipient = $payload['recipient']; $subject = $payload['subject']; + $variables = $payload['variables']; $name = $payload['name']; - $body = $payload['body']; - $from = $payload['from']; + + $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); + + foreach ($variables as $key => $value) { + $body->setParam('{{' . $key . '}}', $value); + } + + $body = $body->render(); /** @var PHPMailer $mail */ - $mail = $register->get('smtp'); - - // Set project mail - /*$register->get('smtp') - ->setFrom( - App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), - ($project->getId() === 'console') - ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')) - : \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name') - ) - );*/ + $mail = empty($smtp) + ? $register->get('smtp') + : $this->getMailer($smtp); $mail->clearAddresses(); $mail->clearAllRecipients(); @@ -74,8 +77,6 @@ class Mails extends Action $mail->clearAttachments(); $mail->clearBCCs(); $mail->clearCCs(); - - $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), (empty($from) ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')) : $from)); $mail->addAddress($recipient, $name); $mail->Subject = $subject; $mail->Body = $body; @@ -87,4 +88,39 @@ class Mails extends Action throw new Exception('Error sending mail: ' . $error->getMessage(), 500); } } + + /** + * @param array $smtp + * @return PHPMailer + * @throws \PHPMailer\PHPMailer\Exception + */ + protected function getMailer(array $smtp): PHPMailer + { + $mail = new PHPMailer(true); + + $mail->isSMTP(); + + $username = $smtp['username']; + $password = $smtp['password']; + + $mail->XMailer = 'Appwrite Mailer'; + $mail->Host = $smtp['host']; + $mail->Port = $smtp['port']; + $mail->SMTPAuth = (!empty($username) && !empty($password)); + $mail->Username = $username; + $mail->Password = $password; + $mail->SMTPSecure = $smtp['secure']; + $mail->SMTPAutoTLS = false; + $mail->CharSet = 'UTF-8'; + + $mail->setFrom($smtp['senderEmail'], $smtp['senderName']); + + if (!empty($smtp['replyTo'])) { + $mail->addReplyTo($smtp['replyTo'], $smtp['senderName']); + } + + $mail->isHTML(); + + return $mail; + } } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 323829b130..f38631c19b 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -47,6 +47,8 @@ class Messaging extends Action } /** + * @param Message $message + * @return void * @throws Exception */ public function action(Message $message): void @@ -54,15 +56,18 @@ class Messaging extends Action $payload = $message->getPayload() ?? []; if (empty($payload)) { - throw new Exception('Missing payload'); + Console::error('Payload arg not found'); + return; } if (empty($payload['recipient'])) { - throw new Exception('Missing recipient'); + Console::error('Recipient arg not found'); + return; } if (empty($payload['message'])) { - throw new Exception('Missing message'); + Console::error('Message arg not found'); + return; } $sms = match ($this->dsn->getHost()) { @@ -75,15 +80,15 @@ class Messaging extends Action default => null }; - $from = App::getEnv('_APP_SMS_FROM'); - if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { - Console::info('Skipped sms processing. No Phone provider has been set.'); + Console::error('Skipped sms processing. No Phone provider has been set.'); return; } + $from = App::getEnv('_APP_SMS_FROM'); + if (empty($from)) { - Console::info('Skipped sms processing. No phone number has been set.'); + Console::error('Skipped sms processing. No phone number has been set.'); return; } diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php new file mode 100644 index 0000000000..da63292aa2 --- /dev/null +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -0,0 +1,330 @@ +desc('Migrations worker') + ->inject('message') + ->inject('getProjectDB') + ->inject('dbForConsole') + ->callback(fn(Message $message, callable $getProjectDB, Database $dbForConsole) => $this->action($message, $getProjectDB, $dbForConsole)); + } + + /** + * @param Message $message + * @param callable $getProjectDB + * @param Database $dbForConsole + * @return void + * @throws Exception + */ + public function action(Message $message, callable $getProjectDB, Database $dbForConsole): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + $events = $payload['events'] ?? []; + $project = new Document($payload['project'] ?? []); + $migration = new Document($payload['migration'] ?? []); + + if ($project->getId() === 'console') { + return; + } + + $this->dbForProject = $getProjectDB($project); + $this->dbForConsole = $dbForConsole; + + /** + * Handle Event execution. + */ + if (! empty($events)) { + return; + } + + $this->processMigration($project, $migration); + } + + /** + * @param string $source + * @param array $credentials + * @return Source + * @throws Exception + */ + protected function processSource(string $source, array $credentials): Source + { + return match ($source) { + Firebase::getName() => new Firebase( + json_decode($credentials['serviceAccount'], true), + ), + Supabase::getName() => new Supabase( + $credentials['endpoint'], + $credentials['apiKey'], + $credentials['databaseHost'], + 'postgres', + $credentials['username'], + $credentials['password'], + $credentials['port'], + ), + NHost::getName() => new NHost( + $credentials['subdomain'], + $credentials['region'], + $credentials['adminSecret'], + $credentials['database'], + $credentials['username'], + $credentials['password'], + $credentials['port'], + ), + Appwrite::getName() => new Appwrite($credentials['projectId'], str_starts_with($credentials['endpoint'], 'http://localhost/v1') ? 'http://appwrite/v1' : $credentials['endpoint'], $credentials['apiKey']), + default => throw new \Exception('Invalid source type'), + }; + } + + /** + * @throws Authorization + * @throws Structure + * @throws Conflict + * @throws \Utopia\Database\Exception + * @throws Exception + */ + protected function updateMigrationDocument(Document $migration, Document $project): Document + { + /** Trigger Realtime */ + $allEvents = Event::generateEvents('migrations.[migrationId].update', [ + 'migrationId' => $migration->getId(), + ]); + + $target = Realtime::fromPayload( + event: $allEvents[0], + payload: $migration, + project: $project + ); + + Realtime::send( + projectId: 'console', + payload: $migration->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'], + ); + + Realtime::send( + projectId: $project->getId(), + payload: $migration->getArrayCopy(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'], + ); + + return $this->dbForProject->updateDocument('migrations', $migration->getId(), $migration); + } + + /** + * @param Document $apiKey + * @return void + * @throws \Utopia\Database\Exception + * @throws Authorization + * @throws Conflict + * @throws Restricted + * @throws Structure + */ + protected function removeAPIKey(Document $apiKey): void + { + $this->dbForConsole->deleteDocument('keys', $apiKey->getId()); + } + + /** + * @param Document $project + * @return Document + * @throws Authorization + * @throws Structure + * @throws \Utopia\Database\Exception + * @throws Exception + */ + protected function generateAPIKey(Document $project): Document + { + $generatedSecret = bin2hex(\random_bytes(128)); + + $key = new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'projectInternalId' => $project->getInternalId(), + 'projectId' => $project->getId(), + 'name' => 'Transfer API Key', + 'scopes' => [ + 'users.read', + 'users.write', + 'teams.read', + 'teams.write', + 'databases.read', + 'databases.write', + 'collections.read', + 'collections.write', + 'documents.read', + 'documents.write', + 'buckets.read', + 'buckets.write', + 'files.read', + 'files.write', + 'functions.read', + 'functions.write', + ], + 'expire' => null, + 'sdks' => [], + 'accessedAt' => null, + 'secret' => $generatedSecret, + ]); + + $this->dbForConsole->createDocument('keys', $key); + $this->dbForConsole->deleteCachedDocument('projects', $project->getId()); + + return $key; + } + + /** + * @param Document $project + * @param Document $migration + * @return void + * @throws Authorization + * @throws Conflict + * @throws Restricted + * @throws Structure + * @throws \Utopia\Database\Exception + */ + protected function processMigration(Document $project, Document $migration): void + { + /** + * @var Document $migrationDocument + * @var Transfer $transfer + */ + $migrationDocument = null; + $transfer = null; + $projectDocument = $this->dbForConsole->getDocument('projects', $project->getId()); + $tempAPIKey = $this->generateAPIKey($projectDocument); + + try { + $migrationDocument = $this->dbForProject->getDocument('migrations', $migration->getId()); + $migrationDocument->setAttribute('stage', 'processing'); + $migrationDocument->setAttribute('status', 'processing'); + $this->updateMigrationDocument($migrationDocument, $projectDocument); + + $source = $this->processSource($migrationDocument->getAttribute('source'), $migrationDocument->getAttribute('credentials')); + + $source->report(); + + $destination = new DestinationsAppwrite( + $projectDocument->getId(), + 'http://appwrite/v1', + $tempAPIKey['secret'], + ); + + $transfer = new Transfer( + $source, + $destination + ); + + /** Start Transfer */ + $migrationDocument->setAttribute('stage', 'migrating'); + $this->updateMigrationDocument($migrationDocument, $projectDocument); + $transfer->run($migrationDocument->getAttribute('resources'), function () use ($migrationDocument, $transfer, $projectDocument) { + $migrationDocument->setAttribute('resourceData', json_encode($transfer->getCache())); + $migrationDocument->setAttribute('statusCounters', json_encode($transfer->getStatusCounters())); + + $this->updateMigrationDocument($migrationDocument, $projectDocument); + }); + + $errors = $transfer->getReport(Resource::STATUS_ERROR); + + if (count($errors) > 0) { + $migrationDocument->setAttribute('status', 'failed'); + $migrationDocument->setAttribute('stage', 'finished'); + + $errorMessages = []; + foreach ($errors as $error) { + $errorMessages[] = "Failed to transfer resource '{$error['id']}:{$error['resource']}' with message '{$error['message']}'"; + } + + $migrationDocument->setAttribute('errors', $errorMessages); + $this->updateMigrationDocument($migrationDocument, $projectDocument); + + return; + } + + $migrationDocument->setAttribute('status', 'completed'); + $migrationDocument->setAttribute('stage', 'finished'); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + + if ($migrationDocument) { + Console::error($th->getMessage()); + Console::error($th->getTraceAsString()); + $migrationDocument->setAttribute('status', 'failed'); + $migrationDocument->setAttribute('stage', 'finished'); + $migrationDocument->setAttribute('errors', [$th->getMessage()]); + + return; + } + + if ($transfer) { + $errors = $transfer->getReport(Resource::STATUS_ERROR); + + if (count($errors) > 0) { + $migrationDocument->setAttribute('status', 'failed'); + $migrationDocument->setAttribute('stage', 'finished'); + $migrationDocument->setAttribute('errors', $errors); + } + } + } finally { + if ($migrationDocument) { + $this->updateMigrationDocument($migrationDocument, $projectDocument); + } + if ($tempAPIKey) { + $this->removeAPIKey($tempAPIKey); + } + } + } +} diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php index 5487a1c8b4..3afc3c5ae5 100644 --- a/src/Appwrite/Platform/Workers/Usage.php +++ b/src/Appwrite/Platform/Workers/Usage.php @@ -42,7 +42,11 @@ class Usage extends Action } /** - * @throws Exception + * @param Message $message + * @param $pools + * @param $cache + * @return void + * @throws \Utopia\Database\Exception */ public function action(Message $message, $pools, $cache): void { @@ -81,10 +85,18 @@ class Usage extends Action } -/** -* On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files. -* When we remove a parent document we need to deduct his children aggregation from the project scope. -*/ + /** + * On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files. + * When we remove a parent document we need to deduct his children aggregation from the project scope. + + * @param $database + * @param $projectInternalId + * @param Document $document + * @param array $metrics + * @param $pools + * @param $cache + * @return void + */ private function reduce($database, $projectInternalId, Document $document, array &$metrics, $pools, $cache) { try { diff --git a/src/Appwrite/Platform/Workers/UsageHook.php b/src/Appwrite/Platform/Workers/UsageHook.php index f0bd0ac2c9..e5a535d9a5 100644 --- a/src/Appwrite/Platform/Workers/UsageHook.php +++ b/src/Appwrite/Platform/Workers/UsageHook.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Workers; use Utopia\App; use Utopia\Database\Database; -use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; use Utopia\Platform\Action; @@ -32,6 +31,12 @@ class UsageHook extends Usage ; } + /** + * @param $register + * @param $cache + * @param $pools + * @return void + */ public function action($register, $cache, $pools): void { Timer::tick(30000, function () use ($register, $cache, $pools) { diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index 85a8e1eae0..dd7b92bf5e 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -10,7 +10,7 @@ use Utopia\Queue\Message; class Webhooks extends Action { - private $errors = []; + private array $errors = []; public static function getName(): string { @@ -29,6 +29,8 @@ class Webhooks extends Action } /** + * @param Message $message + * @return void * @throws Exception */ public function action(Message $message): void @@ -50,14 +52,19 @@ class Webhooks extends Action } } - if (!empty($errors)) { - throw new Exception(\implode(" / \n\n", $errors)); + if (!empty($this->errors)) { + throw new Exception(\implode(" / \n\n", $this->errors)); } - - $this->errors = []; } - + /** + * @param array $events + * @param string $payload + * @param Document $webhook + * @param Document $user + * @param Document $project + * @return void + */ private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void { @@ -67,7 +74,7 @@ class Webhooks extends Action $httpUser = $webhook->getAttribute('httpUser'); $httpPass = $webhook->getAttribute('httpPass'); $ch = \curl_init($webhook->getAttribute('url')); - var_dump($url); + \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); \curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); \curl_setopt($ch, CURLOPT_HEADER, 0); From b1da87f970e22af51bf0e6d069b9d2e69c266272 Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 1 Oct 2023 21:06:25 +0300 Subject: [PATCH 048/144] migrations worker --- src/Appwrite/Platform/Services/Workers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 6ec841bf23..67ecf3dd55 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -33,7 +33,7 @@ class Workers extends Service ->addAction(Deletes::getName(), new Deletes()) ->addAction(UsageHook::getName(), new UsageHook()) ->addAction(Usage::getName(), new Usage()) - ->addAction(Usage::getName(), new Migrations()) + ->addAction(Migrations::getName(), new Migrations()) ; } From 75d8741eaa25b5cbb5f0dffe6c702211df5bc564 Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 1 Oct 2023 21:30:52 +0300 Subject: [PATCH 049/144] sync against master --- app/controllers/api/project.php | 142 ++++++++++------------ app/controllers/api/projects.php | 6 +- app/init.php | 3 +- src/Appwrite/Platform/Workers/Deletes.php | 4 + 4 files changed, 68 insertions(+), 87 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index bd8d9d5f73..d992b759c6 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -18,7 +18,7 @@ use Utopia\Database\DateTime; App::get('/v1/project/usage') ->desc('Get usage stats for a project') - ->groups(['api']) + ->groups(['api', 'usage']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'project') @@ -30,94 +30,76 @@ App::get('/v1/project/usage') ->inject('response') ->inject('dbForProject') ->action(function (string $range, Response $response, Database $dbForProject) { - $usage = []; - if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $periods = [ - '24h' => [ - 'period' => '1h', - 'limit' => 24, - ], - '7d' => [ - 'period' => '1d', - 'limit' => 7, - ], - '30d' => [ - 'period' => '1d', - 'limit' => 30, - ], - '90d' => [ - 'period' => '1d', - 'limit' => 90, - ], - ]; - $metrics = [ - 'project.$all.network.requests', - 'project.$all.network.bandwidth', - 'project.$all.storage.size', - 'users.$all.count.total', - 'databases.$all.count.total', - 'documents.$all.count.total', - 'executions.$all.compute.total', - 'buckets.$all.count.total' - ]; + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + METRIC_NETWORK_REQUESTS, + METRIC_NETWORK_INBOUND, + METRIC_NETWORK_OUTBOUND, + METRIC_EXECUTIONS, + METRIC_DOCUMENTS, + METRIC_DATABASES, + METRIC_USERS, + METRIC_BUCKETS, + METRIC_FILES_STORAGE + ]; - $stats = []; + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); - Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $periods[$range]['limit']; - $period = $periods[$range]['period']; - - $requestDocs = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - // backfill metrics with empty values for graphs - $backfill = $limit - \count($requestDocs); - while ($backfill > 0) { - $last = $limit - $backfill - 1; // array index of last added metric - $diff = match ($period) { // convert period to seconds for unix timestamp math - '1h' => 3600, - '1d' => 86400, - }; - $stats[$metric][] = [ - 'value' => 0, - 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), - ]; - $backfill--; - } - $stats[$metric] = array_reverse($stats[$metric]); + $stats[$metric] = []; + foreach ($results as $result) { + $stats[$metric][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; } - }); + } + }); - $usage = new Document([ - 'range' => $range, - 'requests' => $stats[$metrics[0]] ?? [], - 'network' => $stats[$metrics[1]] ?? [], - 'storage' => $stats[$metrics[2]] ?? [], - 'users' => $stats[$metrics[3]] ?? [], - 'databases' => $stats[$metrics[4]] ?? [], - 'documents' => $stats[$metrics[5]] ?? [], - 'executions' => $stats[$metrics[6]] ?? [], - 'buckets' => $stats[$metrics[7]] ?? [], - ]); + + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric][] = [ + 'value' => $stats[$metric][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; + } } - $response->dynamic($usage, Response::MODEL_USAGE_PROJECT); + + $response->dynamic(new Document([ + 'range' => $range, + 'requestsTotal' => ($usage[$metrics[0]]), + 'network' => ($usage[$metrics[1]] + $usage[$metrics[2]]), + 'executionsTotal' => $usage[$metrics[3]], + 'documentsTotal' => $usage[$metrics[4]], + 'databasesTotal' => $usage[$metrics[5]], + 'usersTotal' => $usage[$metrics[6]], + 'bucketsTotal' => $usage[$metrics[7]], + 'filesStorage' => $usage[$metrics[8]], + ]), Response::MODEL_USAGE_PROJECT); }); + // Variables App::post('/v1/project/variables') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index b8637b5829..ddcc19f4c7 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -721,11 +721,6 @@ App::delete('/v1/projects/:projectId') ->inject('dbForConsole') ->inject('queueForDeletes') ->action(function (string $projectId, Response $response, Document $user, Database $dbForConsole, Delete $queueForDeletes) { - - if (!Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password - throw new Exception(Exception::USER_INVALID_CREDENTIALS); - } - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -997,6 +992,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') $response->noContent(); }); +// Keys App::post('/v1/projects/:projectId/keys') ->desc('Create Key') ->groups(['api', 'projects']) diff --git a/app/init.php b/app/init.php index f22fe089a0..46d90cf124 100644 --- a/app/init.php +++ b/app/init.php @@ -54,7 +54,6 @@ use Utopia\Messaging\Adapters\SMS\Telesign; use Utopia\Messaging\Adapters\SMS\TextMagic; use Utopia\Messaging\Adapters\SMS\Twilio; use Utopia\Messaging\Adapters\SMS\Vonage; -use Utopia\Queue\Server; use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Backblaze; @@ -70,7 +69,6 @@ use Utopia\Pools\Group; use Utopia\Pools\Pool; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; -use Appwrite\Auth\OAuth2\Github; use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Func; @@ -240,6 +238,7 @@ Config::load('providers', __DIR__ . '/config/providers.php'); Config::load('platforms', __DIR__ . '/config/platforms.php'); Config::load('collections', __DIR__ . '/config/collections.php'); Config::load('runtimes', __DIR__ . '/config/runtimes.php'); +Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); Config::load('usage', __DIR__ . '/config/usage.php'); Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index a78e56bed0..7ca1e8564d 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -136,6 +136,10 @@ class Deletes extends Action case DELETE_TYPE_SESSIONS: $this->deleteExpiredSessions($dbForConsole, $getProjectDB); break; + case DELETE_TYPE_CERTIFICATES: + $document = new Document($this->args['document']); + $this->deleteCertificates($document); + break; case DELETE_TYPE_USAGE: $this->deleteUsageStats($dbForConsole, $getProjectDB, $hourlyUsageRetentionDatetime); break; From 9995ccfd51c3e9928e091add11a639b7da7f2c35 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 2 Oct 2023 09:48:06 +0300 Subject: [PATCH 050/144] composer --- composer.lock | 5319 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5319 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..d03e72e6e0 --- /dev/null +++ b/composer.lock @@ -0,0 +1,5319 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "509d6d460bad65092c5e3b8d6b390ec9", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/appwrite", + "version": "10.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-for-php.git", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\": "src/Appwrite" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", + "support": { + "email": "team@appwrite.io", + "issues": "https://github.com/appwrite/sdk-for-php/issues", + "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", + "url": "https://appwrite.io/support" + }, + "time": "2023-09-07T23:28:31+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "f3897169f5c1f365312238a516ae9465f804634f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", + "reference": "f3897169f5c1f365312238a516ae9465f804634f", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" + }, + "time": "2023-02-24T09:50:42+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.13.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "5ab496b3908992b39275994a23783701c4b3de84" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.7.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2023-09-12T19:38:43+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1.4", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-25T09:12:45+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "league/csv", + "version": "9.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.0", + "phpstan/phpstan-phpunit": "^0.12.0", + "phpstan/phpstan-strict-rules": "^0.12.0", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2021-04-17T16:32:08+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.1.5", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/40ca2990dba2c1719e5c62168e822e0b86c167d4", + "reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2023-08-17T16:17:41+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.11.0", + "jean85/pretty-package-versions": "^1.2 || ^2.0.1", + "php": "^7.1 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "squizlabs/php_codesniffer": "^3.6", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + }, + "time": "2021-10-20T22:22:37+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2023-03-06T14:43:22+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.31.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.31.1" + }, + "time": "2023-08-29T11:07:46+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.33.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.33.1" + }, + "time": "2023-08-29T11:07:40+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.43.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "f2626acd42665a9987c94af1c93bf20c28d55c9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/f2626acd42665a9987c94af1c93bf20c28d55c9d", + "reference": "f2626acd42665a9987c94af1c93bf20c28d55c9d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.3.*" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.4.*", + "pcov/clobber": "^2.0", + "phpstan/phpstan": "1.10.*", + "phpunit/phpunit": "^9.4", + "rregeer/phpunit-coverage-check": "^0.3.1", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistence using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.43.2" + }, + "time": "2023-09-07T19:04:33+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/0.3.2" + }, + "time": "2023-07-19T16:39:24+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.31.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.31.0" + }, + "time": "2023-08-30T16:10:04+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + }, + "time": "2023-02-07T05:42:46+00:00" + }, + { + "name": "utopia-php/migration", + "version": "0.3.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/migration.git", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "shasum": "" + }, + "require": { + "appwrite/appwrite": "10.0.*", + "php": "8.*", + "utopia-php/cli": "0.*" + }, + "require-dev": { + "laravel/pint": "1.*", + "phpunit/phpunit": "9.*", + "vlucas/phpdotenv": "5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Migration\\": "src/Migration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to migrate resources between services.", + "keywords": [ + "framework", + "migration", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/0.3.5" + }, + "time": "2023-09-25T16:51:47+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.10.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.3.1" + }, + "time": "2023-09-01T17:25:28+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "dev-integrate-workers", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "04e6f0f08da7382cd20a5a5791a64c92745efa36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/04e6f0f08da7382cd20a5a5791a64c92745efa36", + "reference": "04e6f0f08da7382cd20a5a5791a64c92745efa36", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.31.*", + "utopia-php/queue": "0.5.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/integrate-workers" + }, + "time": "2023-09-27T16:50:44+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "dev-feat-get-worker-start", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", + "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" + }, + "time": "2023-07-12T07:55:31+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.14.0" + }, + "time": "2023-03-15T00:16:34+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.7.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.7.1" + }, + "time": "2023-08-30T09:14:37+00:00" + }, + { + "name": "utopia-php/vcs", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/vcs.git", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "shasum": "" + }, + "require": { + "adhocore/jwt": "^1.1", + "php": ">=8.0", + "utopia-php/cache": "^0.8.0", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\VCS\\": "src/VCS", + "Utopia\\Detector\\": "src/Detector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", + "keywords": [ + "framework", + "php", + "utopia", + "vcs" + ], + "support": { + "issues": "https://github.com/utopia-php/vcs/issues", + "source": "https://github.com/utopia-php/vcs/tree/0.5.0" + }, + "time": "2023-09-13T19:05:52+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.10", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-07-05T14:23:37+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.35.2", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" + }, + "time": "2023-09-14T14:59:50+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + }, + "time": "2023-06-03T09:27:29+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.71", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2023-04-25T20:33:03+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "bcad8d995980440892759db0c32acae7c8e79442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + }, + "time": "2023-09-26T12:28:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-19T04:57:46+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/5.0.2" + }, + "time": "2023-03-20T06:05:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-08-28T11:09:02+00:00" + } + ], + "aliases": [ + { + "package": "utopia-php/platform", + "version": "dev-integrate-workers", + "alias": "0.3.3", + "alias_normalized": "0.3.3.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-get-worker-start", + "alias": "0.5.3", + "alias_normalized": "0.5.3.0" + } + ], + "minimum-stability": "stable", + "stability-flags": { + "utopia-php/platform": 20, + "utopia-php/queue": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From 2bb4d8fb8973246a5cf7b34beb4e7e9332de8d7b Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 2 Oct 2023 17:02:48 +0300 Subject: [PATCH 051/144] sync with 1.4 --- app/controllers/api/account.php | 82 ++++----- app/controllers/api/avatars.php | 43 ++--- app/controllers/api/console.php | 2 +- app/controllers/api/databases.php | 93 +++++------ app/controllers/api/functions.php | 72 ++++---- app/controllers/api/graphql.php | 6 +- app/controllers/api/locale.php | 14 +- app/controllers/api/project.php | 24 +-- app/controllers/api/storage.php | 158 +++++++++--------- app/controllers/api/teams.php | 29 ++-- app/controllers/api/vcs.php | 2 + app/controllers/general.php | 32 +++- app/controllers/web/console.php | 1 - docker-compose.yml | 1 + src/Appwrite/Platform/Workers/Builds.php | 64 ++++--- src/Appwrite/Platform/Workers/Deletes.php | 65 +------ .../Utopia/Response/Model/UsageBuckets.php | 30 +--- .../Utopia/Response/Model/UsageCollection.php | 30 +--- .../Utopia/Response/Model/UsageDatabase.php | 62 +------ .../Utopia/Response/Model/UsageDatabases.php | 102 +---------- .../Utopia/Response/Model/UsageFunction.php | 50 +++--- .../Utopia/Response/Model/UsageFunctions.php | 47 +++--- .../Utopia/Response/Model/UsageProject.php | 14 +- .../Utopia/Response/Model/UsageStorage.php | 66 +------- .../Utopia/Response/Model/UsageUsers.php | 47 +----- 25 files changed, 393 insertions(+), 743 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 7c7230bbf6..1830886be8 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -283,7 +283,7 @@ App::post('/v1/account/sessions/email') }); App::get('/v1/account/sessions/oauth2/:provider') - ->desc('Create OAuth2 Session') + ->desc('Create OAuth2 session') ->groups(['api', 'account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') ->label('scope', 'public') @@ -349,17 +349,17 @@ App::get('/v1/account/sessions/oauth2/:provider') }); App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') - ->desc('OAuth2 Callback') - ->groups(['api', 'account']) + ->desc('OAuth2 callback') + ->groups(['account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') ->label('scope', 'public') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'Login state params.', true) - ->param('error', '', new Text(2048), 'Error code returned from the OAuth2 provider.', true) - ->param('error_description', '', new Text(2048), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) + ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) + ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) ->inject('request') ->inject('response') ->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) { @@ -381,18 +381,18 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') }); App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') - ->desc('OAuth2 Callback') - ->groups(['api', 'account']) + ->desc('OAuth2 callback') + ->groups(['account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') ->label('scope', 'public') ->label('origin', '*') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'Login state params.', true) - ->param('error', '', new Text(2048,), 'Error code returned from the OAuth2 provider.', true) - ->param('error_description', '', new Text(2048), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) + ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) + ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) ->inject('request') ->inject('response') ->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) { @@ -414,7 +414,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') }); App::get('/v1/account/sessions/oauth2/:provider/redirect') - ->desc('OAuth2 Redirect') + ->desc('OAuth2 redirect') ->groups(['api', 'account', 'session']) ->label('error', __DIR__ . '/../../views/general/error.phtml') ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -426,10 +426,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('abuse-key', 'ip:{ip}') ->label('docs', false) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) + ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'OAuth2 state params.', true) - ->param('error', '', new Text(2048), 'Error code returned from the OAuth2 provider.', true) - ->param('error_description', '', new Text(2048), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) + ->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true) + ->param('error_description', '', new Text(2048, 0), 'Human-readable text providing additional information about the error returned from the OAuth2 provider.', true) ->inject('request') ->inject('response') ->inject('project') @@ -866,7 +866,7 @@ App::delete('/v1/account/identities/:identityId') }); App::post('/v1/account/sessions/magic-url') - ->desc('Create Magic URL session') + ->desc('Create magic URL session') ->groups(['api', 'account']) ->label('scope', 'public') ->label('auth.type', 'magic-url') @@ -1084,7 +1084,7 @@ App::post('/v1/account/sessions/magic-url') }); App::put('/v1/account/sessions/magic-url') - ->desc('Create Magic URL session (confirmation)') + ->desc('Create magic URL session (confirmation)') ->groups(['api', 'account', 'session']) ->label('scope', 'public') ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -1204,7 +1204,7 @@ App::put('/v1/account/sessions/magic-url') }); App::post('/v1/account/sessions/phone') - ->desc('Create Phone session') + ->desc('Create phone session') ->groups(['api', 'account']) ->label('scope', 'public') ->label('auth.type', 'phone') @@ -1340,7 +1340,7 @@ App::post('/v1/account/sessions/phone') }); App::put('/v1/account/sessions/phone') - ->desc('Create Phone Session (confirmation)') + ->desc('Create phone session (confirmation)') ->groups(['api', 'account', 'session']) ->label('scope', 'public') ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -1456,7 +1456,7 @@ App::put('/v1/account/sessions/phone') }); App::post('/v1/account/sessions/anonymous') - ->desc('Create Anonymous Session') + ->desc('Create anonymous session') ->groups(['api', 'account', 'auth', 'session']) ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') @@ -1638,7 +1638,7 @@ App::post('/v1/account/jwt') }); App::get('/v1/account') - ->desc('Get Account') + ->desc('Get account') ->groups(['api', 'account']) ->label('scope', 'account') ->label('usage.metric', 'users.{scope}.requests.read') @@ -1659,7 +1659,7 @@ App::get('/v1/account') }); App::get('/v1/account/prefs') - ->desc('Get Account Preferences') + ->desc('Get account preferences') ->groups(['api', 'account']) ->label('scope', 'account') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) @@ -1681,7 +1681,7 @@ App::get('/v1/account/prefs') }); App::get('/v1/account/sessions') - ->desc('List Sessions') + ->desc('List sessions') ->groups(['api', 'account']) ->label('scope', 'account') ->label('usage.metric', 'users.{scope}.requests.read') @@ -1720,7 +1720,7 @@ App::get('/v1/account/sessions') }); App::get('/v1/account/logs') - ->desc('List Logs') + ->desc('List logs') ->groups(['api', 'account']) ->label('scope', 'account') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) @@ -1780,7 +1780,7 @@ App::get('/v1/account/logs') }); App::get('/v1/account/sessions/:sessionId') - ->desc('Get Session') + ->desc('Get session') ->groups(['api', 'account']) ->label('scope', 'account') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) @@ -1824,7 +1824,7 @@ App::get('/v1/account/sessions/:sessionId') }); App::patch('/v1/account/name') - ->desc('Update Name') + ->desc('Update name') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.name') ->label('scope', 'account') @@ -1857,7 +1857,7 @@ App::patch('/v1/account/name') }); App::patch('/v1/account/password') - ->desc('Update Password') + ->desc('Update password') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.password') ->label('scope', 'account') @@ -1881,7 +1881,7 @@ App::patch('/v1/account/password') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $password, string $oldPassword, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) { // Check old password only if its an existing user. if (!empty($user->getAttribute('passwordUpdate')) && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password @@ -1923,7 +1923,7 @@ App::patch('/v1/account/password') }); App::patch('/v1/account/email') - ->desc('Update Email') + ->desc('Update email') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.email') ->label('scope', 'account') @@ -1992,7 +1992,7 @@ App::patch('/v1/account/email') }); App::patch('/v1/account/phone') - ->desc('Update Phone') + ->desc('Update phone') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.phone') ->label('scope', 'account') @@ -2050,7 +2050,7 @@ App::patch('/v1/account/phone') }); App::patch('/v1/account/prefs') - ->desc('Update Preferences') + ->desc('Update preferences') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.prefs') ->label('scope', 'account') @@ -2083,7 +2083,7 @@ App::patch('/v1/account/prefs') }); App::patch('/v1/account/status') - ->desc('Update Status') + ->desc('Update status') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.status') ->label('scope', 'account') @@ -2126,7 +2126,7 @@ App::patch('/v1/account/status') }); App::delete('/v1/account/sessions/:sessionId') - ->desc('Delete Session') + ->desc('Delete session') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].sessions.[sessionId].delete') @@ -2201,7 +2201,7 @@ App::delete('/v1/account/sessions/:sessionId') }); App::patch('/v1/account/sessions/:sessionId') - ->desc('Update OAuth Session (Refresh Tokens)') + ->desc('Update OAuth session (refresh tokens)') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].sessions.[sessionId].update') @@ -2286,7 +2286,7 @@ App::patch('/v1/account/sessions/:sessionId') }); App::delete('/v1/account/sessions') - ->desc('Delete Sessions') + ->desc('Delete sessions') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].sessions.[sessionId].delete') @@ -2346,7 +2346,7 @@ App::delete('/v1/account/sessions') }); App::post('/v1/account/recovery') - ->desc('Create Password Recovery') + ->desc('Create password recovery') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].recovery.[tokenId].create') @@ -2528,7 +2528,7 @@ App::post('/v1/account/recovery') }); App::put('/v1/account/recovery') - ->desc('Create Password Recovery (confirmation)') + ->desc('Create password recovery (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].recovery.[tokenId].update') @@ -2615,7 +2615,7 @@ App::put('/v1/account/recovery') }); App::post('/v1/account/verification') - ->desc('Create Email Verification') + ->desc('Create email verification') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].verification.[tokenId].create') @@ -2775,7 +2775,7 @@ App::post('/v1/account/verification') }); App::put('/v1/account/verification') - ->desc('Create Email Verification (confirmation)') + ->desc('Create email verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].verification.[tokenId].update') @@ -2835,7 +2835,7 @@ App::put('/v1/account/verification') }); App::post('/v1/account/verification/phone') - ->desc('Create Phone Verification') + ->desc('Create phone verification') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].verification.[tokenId].create') @@ -2930,7 +2930,7 @@ App::post('/v1/account/verification/phone') }); App::put('/v1/account/verification/phone') - ->desc('Create Phone Verification (confirmation)') + ->desc('Create phone verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].verification.[tokenId].update') diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 3b93348643..e0d967eb00 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -191,7 +191,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro }; App::get('/v1/avatars/credit-cards/:code') - ->desc('Get Credit Card Icon') + ->desc('Get credit card icon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) @@ -211,7 +211,7 @@ App::get('/v1/avatars/credit-cards/:code') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/browsers/:code') - ->desc('Get Browser Icon') + ->desc('Get browser icon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) @@ -231,7 +231,7 @@ App::get('/v1/avatars/browsers/:code') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/flags/:code') - ->desc('Get Country Flag') + ->desc('Get country flag') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) @@ -251,7 +251,7 @@ App::get('/v1/avatars/flags/:code') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/image') - ->desc('Get Image from URL') + ->desc('Get image from URL') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) @@ -307,7 +307,7 @@ App::get('/v1/avatars/image') }); App::get('/v1/avatars/favicon') - ->desc('Get Favicon') + ->desc('Get favicon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) @@ -449,7 +449,7 @@ App::get('/v1/avatars/favicon') }); App::get('/v1/avatars/qr') - ->desc('Get QR Code') + ->desc('Get QR code') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -489,7 +489,7 @@ App::get('/v1/avatars/qr') }); App::get('/v1/avatars/initials') - ->desc('Get User Initials') + ->desc('Get user initials') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache.resource', 'avatar/initials') @@ -509,12 +509,11 @@ App::get('/v1/avatars/initials') ->action(function (string $name, int $width, int $height, string $background, Response $response, Document $user) { $themes = [ - ['background' => '#FFA1CE'], // Default (Pink) - ['background' => '#FDC584'], // Orange - ['background' => '#94DBD1'], // Green - ['background' => '#A1C4FF'], // Blue - ['background' => '#FFA1CE'], // Pink - ['background' => '#CBB1FC'] // Purple + ['background' => '#FD366E'], // Default (Pink) + ['background' => '#FE9567'], // Orange + ['background' => '#7C67FE'], // Purple + ['background' => '#68A3FE'], // Blue + ['background' => '#85DBD8'], // Mint ]; $name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', '')); @@ -526,11 +525,13 @@ App::get('/v1/avatars/initials') $code = 0; foreach ($words as $key => $w) { - $initials .= $w[0] ?? ''; - $code += (isset($w[0])) ? \ord($w[0]) : 0; + if (ctype_alnum($w[0] ?? '')) { + $initials .= $w[0]; + $code += ord($w[0]); - if ($key == 1) { - break; + if ($key == 1) { + break; + } } } @@ -548,8 +549,8 @@ App::get('/v1/avatars/initials') $punch->newImage($width, $height, 'transparent'); - $draw->setFont(__DIR__ . "/../../assets/fonts/poppins-v9-latin-500.ttf"); - $image->setFont(__DIR__ . "/../../assets/fonts/poppins-v9-latin-500.ttf"); + $draw->setFont(__DIR__ . "/../../assets/fonts/inter-v8-latin-regular.woff2"); + $image->setFont(__DIR__ . "/../../assets/fonts/inter-v8-latin-regular.woff2"); $draw->setFillColor(new ImagickPixel('black')); $draw->setFontSize($fontSize); @@ -724,7 +725,7 @@ App::get('/v1/cards/cloud') $text = new \ImagickDraw(); $text->setTextAlignment(Imagick::ALIGN_CENTER); - $text->setFont(__DIR__ . '/../../../public/fonts/Poppins-Bold.ttf'); + $text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf'); $text->setFillColor(new \ImagickPixel('#FFFFFF')); if (\strlen($name) > 32) { @@ -1108,7 +1109,7 @@ App::get('/v1/cards/cloud-og') $textName = new \ImagickDraw(); $textName->setTextAlignment(Imagick::ALIGN_CENTER); - $textName->setFont(__DIR__ . '/../../../public/fonts/Poppins-Bold.ttf'); + $textName->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf'); $textName->setFillColor(new \ImagickPixel('#FFFFFF')); if (\strlen($name) > 32) { diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index 1bd62a1d4c..5abcd0fa23 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -17,7 +17,7 @@ App::init() App::get('/v1/console/variables') - ->desc('Get Variables') + ->desc('Get variables') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 596ea6171b..06fc5254fe 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -17,12 +17,9 @@ use MaxMind\Db\Reader; use Utopia\App; use Utopia\Audit\Audit; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Database; -use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; -use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; @@ -385,7 +382,7 @@ function updateAttribute( } App::post('/v1/databases') - ->desc('Create Database') + ->desc('Create database') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].create') ->label('scope', 'databases.write') @@ -461,7 +458,7 @@ App::post('/v1/databases') }); App::get('/v1/databases') - ->desc('List Databases') + ->desc('List databases') ->groups(['api', 'database']) ->label('scope', 'databases.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -508,7 +505,7 @@ App::get('/v1/databases') }); App::get('/v1/databases/:databaseId') - ->desc('Get Database') + ->desc('Get database') ->groups(['api', 'database']) ->label('scope', 'databases.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -533,7 +530,7 @@ App::get('/v1/databases/:databaseId') }); App::get('/v1/databases/:databaseId/logs') - ->desc('List Database Logs') + ->desc('List database logs') ->groups(['api', 'database']) ->label('scope', 'databases.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -619,7 +616,7 @@ App::get('/v1/databases/:databaseId/logs') App::put('/v1/databases/:databaseId') - ->desc('Update Database') + ->desc('Update database') ->groups(['api', 'database', 'schema']) ->label('scope', 'databases.write') ->label('event', 'databases.[databaseId].update') @@ -663,7 +660,7 @@ App::put('/v1/databases/:databaseId') }); App::delete('/v1/databases/:databaseId') - ->desc('Delete Database') + ->desc('Delete database') ->groups(['api', 'database', 'schema']) ->label('scope', 'databases.write') ->label('event', 'databases.[databaseId].delete') @@ -707,7 +704,7 @@ App::delete('/v1/databases/:databaseId') }); App::post('/v1/databases/:databaseId/collections') - ->desc('Create Collection') + ->desc('Create collection') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].create') ->label('scope', 'collections.write') @@ -775,7 +772,7 @@ App::post('/v1/databases/:databaseId/collections') App::get('/v1/databases/:databaseId/collections') ->alias('/v1/database/collections', ['databaseId' => 'default']) - ->desc('List Collections') + ->desc('List collections') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -832,7 +829,7 @@ App::get('/v1/databases/:databaseId/collections') App::get('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) - ->desc('Get Collection') + ->desc('Get collection') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -866,7 +863,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') App::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->alias('/v1/database/collections/:collectionId/logs', ['databaseId' => 'default']) - ->desc('List Collection Logs') + ->desc('List collection logs') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -960,7 +957,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') App::put('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) - ->desc('Update Collection') + ->desc('Update collection') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].update') @@ -1028,7 +1025,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') App::delete('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) - ->desc('Delete Collection') + ->desc('Delete collection') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].delete') @@ -1082,7 +1079,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string') ->alias('/v1/database/collections/:collectionId/attributes/string', ['databaseId' => 'default']) - ->desc('Create String Attribute') + ->desc('Create string attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1138,7 +1135,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email') ->alias('/v1/database/collections/:collectionId/attributes/email', ['databaseId' => 'default']) - ->desc('Create Email Attribute') + ->desc('Create email attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1180,7 +1177,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->alias('/v1/database/collections/:collectionId/attributes/enum', ['databaseId' => 'default']) - ->desc('Create Enum Attribute') + ->desc('Create enum attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1238,7 +1235,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->alias('/v1/database/collections/:collectionId/attributes/ip', ['databaseId' => 'default']) - ->desc('Create IP Address Attribute') + ->desc('Create IP address attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1280,7 +1277,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->alias('/v1/database/collections/:collectionId/attributes/url', ['databaseId' => 'default']) - ->desc('Create URL Attribute') + ->desc('Create URL attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1322,7 +1319,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') ->alias('/v1/database/collections/:collectionId/attributes/integer', ['databaseId' => 'default']) - ->desc('Create Integer Attribute') + ->desc('Create integer attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1393,7 +1390,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float') ->alias('/v1/database/collections/:collectionId/attributes/float', ['databaseId' => 'default']) - ->desc('Create Float Attribute') + ->desc('Create float attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1467,7 +1464,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') ->alias('/v1/database/collections/:collectionId/attributes/boolean', ['databaseId' => 'default']) - ->desc('Create Boolean Attribute') + ->desc('Create boolean attribute') ->groups(['api', 'database', 'schema']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1508,7 +1505,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') ->alias('/v1/database/collections/:collectionId/attributes/datetime', ['databaseId' => 'default']) - ->desc('Create DateTime Attribute') + ->desc('Create datetime attribute') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1552,7 +1549,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') ->alias('/v1/database/collections/:collectionId/attributes/relationship', ['databaseId' => 'default']) - ->desc('Create Relationship Attribute') + ->desc('Create relationship attribute') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') @@ -1632,7 +1629,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->alias('/v1/database/collections/:collectionId/attributes', ['databaseId' => 'default']) - ->desc('List Attributes') + ->desc('List attributes') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -1705,7 +1702,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->alias('/v1/database/collections/:collectionId/attributes/:key', ['databaseId' => 'default']) - ->desc('Get Attribute') + ->desc('Get attribute') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -1779,7 +1776,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') - ->desc('Update String Attribute') + ->desc('Update string attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -1818,7 +1815,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') - ->desc('Update Email Attribute') + ->desc('Update email attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -1859,7 +1856,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') - ->desc('Update Enum Attribute') + ->desc('Update enum attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -1902,7 +1899,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/ }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') - ->desc('Update IP Address Attribute') + ->desc('Update IP address attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -1943,7 +1940,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') - ->desc('Update URL Attribute') + ->desc('Update URL attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -1984,7 +1981,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/: }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') - ->desc('Update Integer Attribute') + ->desc('Update integer attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -2035,7 +2032,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') - ->desc('Update Float Attribute') + ->desc('Update float attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -2086,7 +2083,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') - ->desc('Update Boolean Attribute') + ->desc('Update boolean attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -2126,7 +2123,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') - ->desc('Update DateTime Attribute') + ->desc('Update dateTime attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -2166,7 +2163,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet }); App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') - ->desc('Update Relationship Attribute') + ->desc('Update relationship attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') @@ -2222,7 +2219,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->alias('/v1/database/collections/:collectionId/attributes/:key', ['databaseId' => 'default']) - ->desc('Delete Attribute') + ->desc('Delete attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete') @@ -2332,7 +2329,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->alias('/v1/database/collections/:collectionId/indexes', ['databaseId' => 'default']) - ->desc('Create Index') + ->desc('Create index') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create') ->label('scope', 'collections.write') @@ -2490,7 +2487,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->alias('/v1/database/collections/:collectionId/indexes', ['databaseId' => 'default']) - ->desc('List Indexes') + ->desc('List indexes') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -2553,7 +2550,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) - ->desc('Get Index') + ->desc('Get index') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -2592,7 +2589,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) - ->desc('Delete Index') + ->desc('Delete index') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete') @@ -2656,7 +2653,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) - ->desc('Create Document') + ->desc('Create document') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') ->label('scope', 'documents.write') @@ -2895,7 +2892,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) - ->desc('List Documents') + ->desc('List documents') ->groups(['api', 'database']) ->label('scope', 'documents.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -3020,7 +3017,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) - ->desc('Get Document') + ->desc('Get document') ->groups(['api', 'database']) ->label('scope', 'documents.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -3113,7 +3110,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') ->alias('/v1/database/collections/:collectionId/documents/:documentId/logs', ['databaseId' => 'default']) - ->desc('List Document Logs') + ->desc('List document logs') ->groups(['api', 'database']) ->label('scope', 'documents.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -3212,7 +3209,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) - ->desc('Update Document') + ->desc('Update document') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update') ->label('scope', 'documents.write') @@ -3440,7 +3437,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) - ->desc('Delete Document') + ->desc('Delete document') ->groups(['api', 'database']) ->label('scope', 'documents.write') ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 8fcee70530..b510f37b5f 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -13,7 +13,6 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Messaging\Adapter\Realtime; use Utopia\Validator\Assoc; -use Appwrite\Usage\Stats; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -84,6 +83,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project Permission::delete(Role::any()), ], 'resourceId' => $function->getId(), + 'resourceInternalId' => $function->getInternalId(), 'resourceType' => 'functions', 'entrypoint' => $entrypoint, 'commands' => $function->getAttribute('commands', ''), @@ -120,7 +120,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project App::post('/v1/functions') ->groups(['api', 'functions']) - ->desc('Create Function') + ->desc('Create function') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].create') ->label('audits.event', 'function.create') @@ -339,7 +339,7 @@ App::post('/v1/functions') App::get('/v1/functions') ->groups(['api', 'functions']) - ->desc('List Functions') + ->desc('List functions') ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') @@ -414,7 +414,7 @@ App::get('/v1/functions/runtimes') App::get('/v1/functions/:functionId') ->groups(['api', 'functions']) - ->desc('Get Function') + ->desc('Get function') ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') @@ -437,7 +437,7 @@ App::get('/v1/functions/:functionId') }); App::get('/v1/functions/:functionId/usage') - ->desc('Get Function Usage') + ->desc('Get function usage') ->groups(['api', 'functions', 'usage']) ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -600,7 +600,7 @@ App::get('/v1/functions/usage') App::put('/v1/functions/:functionId') ->groups(['api', 'functions']) - ->desc('Update Function') + ->desc('Update function') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].update') ->label('audits.event', 'function.update') @@ -865,7 +865,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') App::patch('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) - ->desc('Update Function Deployment') + ->desc('Update function deployment') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].update') ->label('audits.event', 'deployment.update') @@ -881,10 +881,9 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') ->param('deploymentId', '', new UID(), 'Deployment ID.') ->inject('response') ->inject('dbForProject') - ->inject('project') ->inject('queueForEvents') ->inject('dbForConsole') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForConsole) { + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -928,7 +927,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') App::delete('/v1/functions/:functionId') ->groups(['api', 'functions']) - ->desc('Delete Function') + ->desc('Delete function') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].delete') ->label('audits.event', 'function.delete') @@ -944,9 +943,8 @@ App::delete('/v1/functions/:functionId') ->inject('dbForProject') ->inject('queueForDeletes') ->inject('queueForEvents') - ->inject('project') ->inject('dbForConsole') - ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Document $project, Database $dbForConsole) { + ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -976,7 +974,7 @@ App::delete('/v1/functions/:functionId') App::post('/v1/functions/:functionId/deployments') ->groups(['api', 'functions']) - ->desc('Create Deployment') + ->desc('Create deployment') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].create') ->label('audits.event', 'deployment.create') @@ -1002,9 +1000,8 @@ App::post('/v1/functions/:functionId/deployments') ->inject('project') ->inject('deviceFunctions') ->inject('deviceLocal') - ->inject('dbForConsole') ->inject('queueForBuilds') - ->action(function (string $functionId, string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Database $dbForConsole, Build $queueForBuilds) { + ->action(function (string $functionId, string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Build $queueForBuilds) { $activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN); @@ -1038,7 +1035,7 @@ App::post('/v1/functions/:functionId/deployments') } $fileExt = new FileExt([FileExt::TYPE_GZIP]); - $fileSizeValidator = new FileSize(App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', 0)); + $fileSizeValidator = new FileSize(App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000')); $upload = new Upload(); // Make sure we handle a single file and multiple files the same way @@ -1197,7 +1194,7 @@ App::post('/v1/functions/:functionId/deployments') App::get('/v1/functions/:functionId/deployments') ->groups(['api', 'functions']) - ->desc('List Deployments') + ->desc('List deployments') ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') @@ -1267,7 +1264,7 @@ App::get('/v1/functions/:functionId/deployments') App::get('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) - ->desc('Get Deployment') + ->desc('Get deployment') ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') @@ -1309,7 +1306,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId') App::delete('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) - ->desc('Delete Deployment') + ->desc('Delete deployment') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].delete') ->label('audits.event', 'deployment.delete') @@ -1373,7 +1370,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->groups(['api', 'functions']) - ->desc('Create Build') + ->desc('Create build') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].update') ->label('audits.event', 'deployment.update') @@ -1441,7 +1438,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') App::post('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) - ->desc('Create Execution') + ->desc('Create execution') ->label('scope', 'execution.write') ->label('event', 'functions.[functionId].executions.[executionId].create') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -1452,7 +1449,7 @@ App::post('/v1/functions/:functionId/executions') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_EXECUTION) ->param('functionId', '', new UID(), 'Function ID.') - ->param('body', '', new Text(8192, 0), 'HTTP body of execution. Default value is empty string.', true) + ->param('body', '', new Text(0, 0), 'HTTP body of execution. Default value is empty string.', true) ->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true) ->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true) ->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true) @@ -1683,13 +1680,7 @@ App::post('/v1/functions/:functionId/executions') $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); - /** - * Sync execution compute usage from - */ - $queueForUsage - ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function - ; + } catch (\Throwable $th) { $durationEnd = \microtime(true); @@ -1706,13 +1697,10 @@ App::post('/v1/functions/:functionId/executions') $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); } - // TODO revise this later using route label $queueForUsage - ->setParam('functionId', $function->getId()) - ->setParam('executions.{scope}.compute', 1) - ->setParam('executionStatus', $execution->getAttribute('status', '')) - ->setParam('executionTime', $execution->getAttribute('duration')); // ms - + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function + ; $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); @@ -1738,7 +1726,7 @@ App::post('/v1/functions/:functionId/executions') App::get('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) - ->desc('List Executions') + ->desc('List executions') ->label('scope', 'execution.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'functions') @@ -1813,7 +1801,7 @@ App::get('/v1/functions/:functionId/executions') App::get('/v1/functions/:functionId/executions/:executionId') ->groups(['api', 'functions']) - ->desc('Get Execution') + ->desc('Get execution') ->label('scope', 'execution.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'functions') @@ -1861,7 +1849,7 @@ App::get('/v1/functions/:functionId/executions/:executionId') // Variables App::post('/v1/functions/:functionId/variables') - ->desc('Create Variable') + ->desc('Create variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') ->label('audits.event', 'variable.create') @@ -1925,7 +1913,7 @@ App::post('/v1/functions/:functionId/variables') }); App::get('/v1/functions/:functionId/variables') - ->desc('List Variables') + ->desc('List variables') ->groups(['api', 'functions']) ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -1952,7 +1940,7 @@ App::get('/v1/functions/:functionId/variables') }); App::get('/v1/functions/:functionId/variables/:variableId') - ->desc('Get Variable') + ->desc('Get variable') ->groups(['api', 'functions']) ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -1991,7 +1979,7 @@ App::get('/v1/functions/:functionId/variables/:variableId') }); App::put('/v1/functions/:functionId/variables/:variableId') - ->desc('Update Variable') + ->desc('Update variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') ->label('audits.event', 'variable.update') @@ -2052,7 +2040,7 @@ App::put('/v1/functions/:functionId/variables/:variableId') }); App::delete('/v1/functions/:functionId/variables/:variableId') - ->desc('Delete Variable') + ->desc('Delete variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') ->label('audits.event', 'variable.delete') diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 8ee3b5ac47..830aecbe0c 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -18,7 +18,7 @@ use Utopia\Validator\JSON; use Utopia\Validator\Text; App::get('/v1/graphql') - ->desc('GraphQL Endpoint') + ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') ->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) @@ -58,7 +58,7 @@ App::get('/v1/graphql') }); App::post('/v1/graphql/mutation') - ->desc('GraphQL Endpoint') + ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') ->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) @@ -103,7 +103,7 @@ App::post('/v1/graphql/mutation') }); App::post('/v1/graphql') - ->desc('GraphQL Endpoint') + ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') ->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php index 29b4932543..53f0c5bb25 100644 --- a/app/controllers/api/locale.php +++ b/app/controllers/api/locale.php @@ -9,7 +9,7 @@ use Utopia\Database\Document; use Utopia\Locale\Locale; App::get('/v1/locale') - ->desc('Get User Locale') + ->desc('Get user locale') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -91,7 +91,7 @@ App::get('/v1/locale/codes') }); App::get('/v1/locale/countries') - ->desc('List Countries') + ->desc('List countries') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -124,7 +124,7 @@ App::get('/v1/locale/countries') }); App::get('/v1/locale/countries/eu') - ->desc('List EU Countries') + ->desc('List EU countries') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -159,7 +159,7 @@ App::get('/v1/locale/countries/eu') }); App::get('/v1/locale/countries/phones') - ->desc('List Countries Phone Codes') + ->desc('List countries phone codes') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -193,7 +193,7 @@ App::get('/v1/locale/countries/phones') }); App::get('/v1/locale/continents') - ->desc('List Continents') + ->desc('List continents') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -225,7 +225,7 @@ App::get('/v1/locale/continents') }); App::get('/v1/locale/currencies') - ->desc('List Currencies') + ->desc('List currencies') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -248,7 +248,7 @@ App::get('/v1/locale/currencies') App::get('/v1/locale/languages') - ->desc('List Languages') + ->desc('List languages') ->groups(['api', 'locale']) ->label('scope', 'locale.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index d992b759c6..0d0909fb0a 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -3,6 +3,7 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; use Utopia\App; +use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -14,7 +15,6 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -use Utopia\Database\DateTime; App::get('/v1/project/usage') ->desc('Get usage stats for a project') @@ -72,18 +72,18 @@ App::get('/v1/project/usage') '1d' => 'Y-m-d\T00:00:00.000P', }; - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, - ]; - } + foreach ($metrics as $metric) { + $usage[$metric] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric][] = [ + 'value' => $stats[$metric][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; } + } $response->dynamic(new Document([ diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 0976d3c15b..427f360f78 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -192,7 +192,7 @@ App::get('/v1/storage/buckets') }); App::get('/v1/storage/buckets/:bucketId') - ->desc('Get Bucket') + ->desc('Get bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -217,7 +217,7 @@ App::get('/v1/storage/buckets/:bucketId') }); App::put('/v1/storage/buckets/:bucketId') - ->desc('Update Bucket') + ->desc('Update bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') ->label('event', 'buckets.[bucketId].update') @@ -284,7 +284,7 @@ App::put('/v1/storage/buckets/:bucketId') }); App::delete('/v1/storage/buckets/:bucketId') - ->desc('Delete Bucket') + ->desc('Delete bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') ->label('audits.event', 'bucket.delete') @@ -326,7 +326,7 @@ App::delete('/v1/storage/buckets/:bucketId') App::post('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) - ->desc('Create File') + ->desc('Create file') ->groups(['api', 'storage']) ->label('scope', 'files.write') ->label('audits.event', 'file.create') @@ -678,7 +678,7 @@ App::post('/v1/storage/buckets/:bucketId/files') App::get('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) - ->desc('List Files') + ->desc('List files') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -757,7 +757,7 @@ App::get('/v1/storage/buckets/:bucketId/files') App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) - ->desc('Get File') + ->desc('Get file') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -804,7 +804,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default']) - ->desc('Get File Preview') + ->desc('Get file preview') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('cache', true) @@ -971,7 +971,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default']) - ->desc('Get File for Download') + ->desc('Get file for download') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -1112,7 +1112,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default']) - ->desc('Get File for View') + ->desc('Get file for view') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -1263,7 +1263,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) - ->desc('Update File') + ->desc('Update file') ->groups(['api', 'storage']) ->label('scope', 'files.write') ->label('event', 'buckets.[bucketId].files.[fileId].update') @@ -1466,6 +1466,75 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $response->noContent(); }); +App::get('/v1/storage/usage') + ->desc('Get usage stats for storage') + ->groups(['api', 'storage']) + ->label('scope', 'files.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'storage') + ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USAGE_STORAGE) + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForProject') + ->action(function (string $range, Response $response, Database $dbForProject) { + + $periods = Config::getParam('usage', []); + $stats = $usage = []; + $days = $periods[$range]; + $metrics = [ + METRIC_BUCKETS, + METRIC_FILES, + METRIC_FILES_STORAGE, + ]; + + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $days['limit']; + $period = $days['period']; + $results = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + $stats[$metric] = []; + foreach ($results as $result) { + $stats[$metric][$result->getAttribute('time')] = [ + 'value' => $result->getAttribute('value'), + ]; + } + } + }); + + $format = match ($days['period']) { + '1h' => 'Y-m-d\TH:00:00.000P', + '1d' => 'Y-m-d\T00:00:00.000P', + }; + + foreach ($metrics as $metric) { + $usage[$metric] = []; + $leap = time() - ($days['limit'] * $days['factor']); + while ($leap < time()) { + $leap += $days['factor']; + $formatDate = date($format, $leap); + $usage[$metric][] = [ + 'value' => $stats[$metric][$formatDate]['value'] ?? 0, + 'date' => $formatDate, + ]; + } + } + + $response->dynamic(new Document([ + 'range' => $range, + 'bucketsTotal' => $usage[$metrics[0]], + 'filesTotal' => $usage[$metrics[1]], + 'filesStorage' => $usage[$metrics[2]], + ]), Response::MODEL_USAGE_STORAGE); + }); + App::get('/v1/storage/:bucketId/usage') ->desc('Get usage stats for storage bucket') ->groups(['api', 'storage']) @@ -1540,72 +1609,3 @@ App::get('/v1/storage/:bucketId/usage') 'filesStorage' => $usage[$metrics[1]], ]), Response::MODEL_USAGE_BUCKETS); }); - -App::get('/v1/storage/usage') - ->desc('Get usage stats for storage') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getUsage') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_USAGE_STORAGE) - ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) - ->inject('response') - ->inject('dbForProject') - ->action(function (string $range, Response $response, Database $dbForProject) { - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_BUCKETS, - METRIC_FILES, - METRIC_FILES_STORAGE, - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, - ]; - } - } - - $response->dynamic(new Document([ - 'range' => $range, - 'bucketsTotal' => $usage[$metrics[0]], - 'filesTotal' => $usage[$metrics[1]], - 'filesStorage' => $usage[$metrics[2]], - ]), Response::MODEL_USAGE_STORAGE); - }); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index f52121743a..99fcfeb38c 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -41,7 +41,7 @@ use Utopia\Validator\Assoc; use Utopia\Validator\Text; App::post('/v1/teams') - ->desc('Create Team') + ->desc('Create team') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].create') ->label('scope', 'teams.write') @@ -129,7 +129,7 @@ App::post('/v1/teams') }); App::get('/v1/teams') - ->desc('List Teams') + ->desc('List teams') ->groups(['api', 'teams']) ->label('scope', 'teams.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -181,7 +181,7 @@ App::get('/v1/teams') }); App::get('/v1/teams/:teamId') - ->desc('Get Team') + ->desc('Get team') ->groups(['api', 'teams']) ->label('scope', 'teams.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -208,7 +208,7 @@ App::get('/v1/teams/:teamId') }); App::get('/v1/teams/:teamId/prefs') - ->desc('Get Team Preferences') + ->desc('Get team preferences') ->groups(['api', 'teams']) ->label('scope', 'teams.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) @@ -236,7 +236,7 @@ App::get('/v1/teams/:teamId/prefs') }); App::put('/v1/teams/:teamId') - ->desc('Update Name') + ->desc('Update name') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update') ->label('scope', 'teams.write') @@ -279,7 +279,7 @@ App::put('/v1/teams/:teamId') }); App::put('/v1/teams/:teamId/prefs') - ->desc('Update Preferences') + ->desc('Update preferences') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update.prefs') ->label('scope', 'teams.write') @@ -315,7 +315,7 @@ App::put('/v1/teams/:teamId/prefs') }); App::delete('/v1/teams/:teamId') - ->desc('Delete Team') + ->desc('Delete team') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].delete') ->label('scope', 'teams.write') @@ -357,7 +357,7 @@ App::delete('/v1/teams/:teamId') }); App::post('/v1/teams/:teamId/memberships') - ->desc('Create Team Membership') + ->desc('Create team membership') ->groups(['api', 'teams', 'auth']) ->label('event', 'teams.[teamId].memberships.[membershipId].create') ->label('scope', 'teams.write') @@ -666,7 +666,7 @@ App::post('/v1/teams/:teamId/memberships') }); App::get('/v1/teams/:teamId/memberships') - ->desc('List Team Memberships') + ->desc('List team memberships') ->groups(['api', 'teams']) ->label('scope', 'teams.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -750,7 +750,7 @@ App::get('/v1/teams/:teamId/memberships') }); App::get('/v1/teams/:teamId/memberships/:membershipId') - ->desc('Get Team Membership') + ->desc('Get team membership') ->groups(['api', 'teams']) ->label('scope', 'teams.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) @@ -792,7 +792,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') }); App::patch('/v1/teams/:teamId/memberships/:membershipId') - ->desc('Update Membership') + ->desc('Update membership') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update') ->label('scope', 'teams.write') @@ -863,7 +863,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') }); App::patch('/v1/teams/:teamId/memberships/:membershipId/status') - ->desc('Update Team Membership Status') + ->desc('Update team membership status') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update.status') ->label('scope', 'public') @@ -998,7 +998,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') }); App::delete('/v1/teams/:teamId/memberships/:membershipId') - ->desc('Delete Team Membership') + ->desc('Delete team membership') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].delete') ->label('scope', 'teams.write') @@ -1064,7 +1064,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') }); App::get('/v1/teams/:teamId/logs') - ->desc('List Team Logs') + ->desc('List team logs') ->groups(['api', 'teams']) ->label('scope', 'teams.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1096,6 +1096,7 @@ App::get('/v1/teams/:teamId/logs') $audit = new Audit($dbForProject); $resource = 'team/' . $team->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); + $output = []; foreach ($logs as $i => &$log) { diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 2abf4ef30c..b0050c61d4 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -50,6 +50,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $functionId = $resource->getAttribute('resourceId'); $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $functionInternalId = $function->getInternalId(); $deploymentId = ID::unique(); $repositoryId = $resource->getId(); @@ -173,6 +174,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId Permission::delete(Role::any()), ], 'resourceId' => $functionId, + 'resourceInternalId' => $functionInternalId, 'resourceType' => 'functions', 'entrypoint' => $function->getAttribute('entrypoint'), 'commands' => $function->getAttribute('commands'), diff --git a/app/controllers/general.php b/app/controllers/general.php index 50da07ead6..d0189621cc 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -47,6 +47,8 @@ Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleRequest, Request $request, Response $response) { + $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); + $host = $request->getHostname() ?? ''; $route = Authorization::skip( @@ -57,12 +59,25 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques )[0] ?? null; if ($route === null) { + if ($host === App::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); + } + + if (\str_ends_with($host, App::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); + } + + if (App::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { + if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); + } + } + // Act as API - no Proxy logic + $utopia->getRoute()?->label('error', ''); return false; } - $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); - $projectId = $route->getAttribute('projectId'); $project = Authorization::skip( fn () => $dbForConsole->getDocument('projects', $projectId) @@ -83,6 +98,16 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques $type = $route->getAttribute('resourceType'); if ($type === 'function') { + if (App::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS + if ($request->getProtocol() !== 'https') { + if ($request->getMethod() !== Request::METHOD_GET) { + throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); + } + + return $response->redirect('https://' . $request->getHostname() . $request->getURI()); + } + } + $functionId = $route->getAttribute('resourceId'); $projectId = $route->getAttribute('projectId'); @@ -164,6 +189,7 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); } + $utopia->getRoute()?->label('error', ''); return false; } @@ -381,7 +407,7 @@ App::init() if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations if ($request->getMethod() !== Request::METHOD_GET) { - throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP.'); + throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); } return $response->redirect('https://' . $request->getHostname() . $request->getURI()); diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index 4a6f15df3a..dcf9c80a51 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -1,6 +1,5 @@ inject('queueForUsage') ->inject('cache') ->inject('getProjectDB') - ->inject('deviceFunctions') - ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $deviceFunctions) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $queueForUsage, $cache, $getProjectDB, $deviceFunctions)); + ->inject('getFunctionsDevice') + ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $queueForUsage, $cache, $getProjectDB, $getFunctionsDevice)); } /** @@ -62,12 +62,11 @@ class Builds extends Action * @param Usage $queueForUsage * @param Cache $cache * @param callable $getProjectDB - * @param callable $deviceFunctions + * @param callable $getFunctionsDevice * @return void * @throws \Utopia\Database\Exception - * @throws Exception */ - public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $deviceFunctions): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice): void { $payload = $message->getPayload() ?? []; @@ -86,7 +85,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($deviceFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $getProjectDB, $github, $project, $resource, $deployment, $template); + $this->buildDeployment($getFunctionsDevice, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $getProjectDB, $github, $project, $resource, $deployment, $template); break; default: @@ -95,7 +94,7 @@ class Builds extends Action } /** - * @param callable $deviceFunctions + * @param callable $getFunctionsDevice * @param Func $queueForFunctions * @param Event $queueForEvents * @param Usage $queueForUsage @@ -108,8 +107,9 @@ class Builds extends Action * @param Document $template * @return void * @throws \Utopia\Database\Exception + * @throws Exception */ - protected function buildDeployment(callable $deviceFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void + protected function buildDeployment(callable $getFunctionsDevice, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); @@ -290,7 +290,7 @@ class Builds extends Action Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); - $deviceFunctions = $deviceFunctions($project->getId()); + $deviceFunctions = $getFunctionsDevice($project->getId()); $localDevice = new Local(); $buffer = $localDevice->read($tmpPathFile); @@ -382,33 +382,30 @@ class Builds extends Action $command = \str_replace('"', '\\"', $command); $response = null; - $err = null; - // TODO: Remove run() wrapper when switching to new utopia queue. That should be done on Swoole adapter in the libary - Co\run(function () use ($executor, $project, $deployment, &$response, $source, $function, $runtime, $vars, $command, &$build, $dbForProject, $allEvents, &$err) { - Co::join([ - Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) { - try { - $version = $function->getAttribute('version', 'v2'); - $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'; + Co::join([ + Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) { + try { + $version = $function->getAttribute('version', 'v2'); + $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'; - $response = $executor->createRuntime( - deploymentId: $deployment->getId(), - projectId: $project->getId(), - source: $source, - image: $runtime['image'], - version: $version, - remove: true, - entrypoint: $deployment->getAttribute('entrypoint'), - destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", - variables: $vars, - command: $command - ); - } catch (Exception $error) { - $err = $error; - } - }), + $response = $executor->createRuntime( + deploymentId: $deployment->getId(), + projectId: $project->getId(), + source: $source, + image: $runtime['image'], + version: $version, + remove: true, + entrypoint: $deployment->getAttribute('entrypoint'), + destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", + variables: $vars, + command: $command + ); + } catch (Exception $error) { + $err = $error; + } + }), Co\go(function () use ($executor, $project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err) { try { $executor->getLogs( @@ -451,7 +448,6 @@ class Builds extends Action } }), ]); - }); if ($err) { throw $err; diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 7ca1e8564d..e76dd29032 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -136,10 +136,6 @@ class Deletes extends Action case DELETE_TYPE_SESSIONS: $this->deleteExpiredSessions($dbForConsole, $getProjectDB); break; - case DELETE_TYPE_CERTIFICATES: - $document = new Document($this->args['document']); - $this->deleteCertificates($document); - break; case DELETE_TYPE_USAGE: $this->deleteUsageStats($dbForConsole, $getProjectDB, $hourlyUsageRetentionDatetime); break; @@ -419,15 +415,6 @@ class Deletes extends Action $projectId = $document->getId(); $projectInternalId = $document->getInternalId(); - // Delete project certificates - $domains = $dbForConsole->find('domains', [ - Query::equal('projectInternalId', [$projectInternalId]) - ]); - - foreach ($domains as $domain) { - $this->deleteCertificates($dbForConsole, $domain); - } - // Delete project tables $dbForProject = $getProjectDB($document); @@ -483,54 +470,6 @@ class Deletes extends Action $cache->delete($cache->getRoot(), true); } - /** - * @param Database $dbForConsole - * @param Document $document certificates document - * @return void - * @throws Exception - */ - protected function deleteCertificates(Database $dbForConsole, Document $document): void - { - // If domain has certificate generated - if (isset($document['certificateId'])) { - $domainUsingCertificate = $dbForConsole->findOne('domains', [ - Query::equal('certificateId', [$document['certificateId']]) - ]); - - if (!$domainUsingCertificate) { - $mainDomain = App::getEnv('_APP_DOMAIN_TARGET', ''); - if ($mainDomain === $document->getAttribute('domain')) { - $domainUsingCertificate = $mainDomain; - } - } - - // If certificate is still used by some domain, mark we can't delete. - // Current domain should not be found, because we only have copy. Original domain is already deleted from database. - if ($domainUsingCertificate) { - Console::warning("Skipping certificate deletion, because a domain is still using it."); - return; - } - } - - $domain = $document->getAttribute('domain'); - $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; - $checkTraversal = realpath($directory) === $directory; - - if ($domain && $checkTraversal && is_dir($directory)) { - // Delete certificate document, so Appwrite is aware of change - if (isset($document['certificateId'])) { - $dbForConsole->deleteDocument('certificates', $document['certificateId']); - } - - // Delete files, so Traefik is aware of change - array_map('unlink', glob($directory . '/*.*')); - rmdir($directory); - Console::info("Deleted certificate files for {$domain}"); - } else { - Console::info("No certificate files found for {$domain}"); - } - } - /** * @param callable $getProjectDB * @param Document $document user document @@ -786,7 +725,7 @@ class Deletes extends Action * Request executor to delete all deployment containers */ Console::info("Requesting executor to delete all deployment containers for function " . $functionId); - $this->deleteRuntimes($document, $project); + $this->deleteRuntimes($getProjectDB, $document, $project); } /** @@ -909,7 +848,6 @@ class Deletes extends Action $count = 0; $chunk = 0; $limit = 50; - $results = []; $sum = $limit; $executionStart = \microtime(true); @@ -947,7 +885,6 @@ class Deletes extends Action $count = 0; $chunk = 0; $limit = 50; - $results = []; $sum = $limit; $executionStart = \microtime(true); diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php index 8c6c81f234..83b8744760 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -16,7 +16,7 @@ class UsageBuckets extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('filesCount', [ + ->addRule('filesTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of files in this bucket.', 'default' => [], @@ -30,34 +30,6 @@ class UsageBuckets extends Model 'example' => [], 'array' => true ]) - ->addRule('filesCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageCollection.php b/src/Appwrite/Utopia/Response/Model/UsageCollection.php index 8b6966fcd2..5abcf46b7d 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageCollection.php +++ b/src/Appwrite/Utopia/Response/Model/UsageCollection.php @@ -16,41 +16,13 @@ class UsageCollection extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('documentsCount', [ + ->addRule('documentsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documentsCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index 0c84d796ba..58d49c506e 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -16,72 +16,16 @@ class UsageDatabase extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('documentsCount', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of documents.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsCount', [ + ->addRule('collectionsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of collections.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documentsCreate', [ + ->addRule('documentsTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections delete.', + 'description' => 'Aggregated stats for total number of documents.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php index 93488a47db..a6008ca9e6 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php @@ -16,111 +16,27 @@ class UsageDatabases extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('databasesCount', [ + ->addRule('databasesTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documentsCount', [ + ->addRule('collectionsTotal', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for total number of collections.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('collectionsCount', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of collections.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('databasesCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('databasesRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('databasesUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('databasesDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of collections.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('documentsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for documents deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('collectionsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for collections delete.', - 'default' => [], - 'example' => [], - 'array' => true - ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageFunction.php b/src/Appwrite/Utopia/Response/Model/UsageFunction.php index 58d76bbf41..03acaa750a 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageFunction.php +++ b/src/Appwrite/Utopia/Response/Model/UsageFunction.php @@ -16,30 +16,16 @@ class UsageFunction extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('executionsTotal', [ + ->addRule('deploymentsTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of function executions.', + 'description' => 'Aggregated stats for number of function deployments.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('executionsFailure', [ + ->addRule('deploymentsStorage', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution failures.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('executionsSuccess', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution successes.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('executionsTime', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution duration.', + 'description' => 'Aggregated stats for function deployments storage.', 'default' => [], 'example' => [], 'array' => true @@ -51,23 +37,31 @@ class UsageFunction extends Model 'example' => [], 'array' => true ]) - ->addRule('buildsFailure', [ + ->addRule('buildsStorage', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build failures.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('buildsSuccess', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build successes.', + 'description' => 'Aggregated stats for builds storage.', 'default' => [], 'example' => [], 'array' => true ]) ->addRule('buildsTime', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build duration.', + 'description' => 'Aggregated stats for function build compute.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('executionsTotal', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for number of function executions.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + + ->addRule('executionsTime', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function execution compute.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageFunctions.php b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php index 7adb0d4aa3..6ab36e21ac 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageFunctions.php +++ b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php @@ -16,30 +16,23 @@ class UsageFunctions extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('executionsTotal', [ + ->addRule('functionsTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of function executions.', + 'description' => 'Aggregated stats for number of functions.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('executionsFailure', [ + ->addRule('deploymentsTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution failures.', + 'description' => 'Aggregated stats for number of function deployments.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('executionsSuccess', [ + ->addRule('deploymentsStorage', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution successes.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('executionsTime', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution duration.', + 'description' => 'Aggregated stats for function deployments storage.', 'default' => [], 'example' => [], 'array' => true @@ -51,23 +44,31 @@ class UsageFunctions extends Model 'example' => [], 'array' => true ]) - ->addRule('buildsFailure', [ + ->addRule('buildsStorage', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build failures.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('buildsSuccess', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build successes.', + 'description' => 'Aggregated stats for builds storage.', 'default' => [], 'example' => [], 'array' => true ]) ->addRule('buildsTime', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build duration.', + 'description' => 'Aggregated stats for function build compute.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('executionsTotal', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for number of function executions.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + + ->addRule('executionsTime', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function execution compute.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index e37bc5928d..641613809a 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -16,7 +16,7 @@ class UsageProject extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('requests', [ + ->addRule('requestsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of requests.', 'default' => [], @@ -30,42 +30,42 @@ class UsageProject extends Model 'example' => [], 'array' => true ]) - ->addRule('executions', [ + ->addRule('executionsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for function executions.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documents', [ + ->addRule('documentsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of documents.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('databases', [ + ->addRule('databasesTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of databases.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('users', [ + ->addRule('usersTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of users.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('storage', [ + ->addRule('filesStorage', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('buckets', [ + ->addRule('bucketsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of buckets.', 'default' => [], diff --git a/src/Appwrite/Utopia/Response/Model/UsageStorage.php b/src/Appwrite/Utopia/Response/Model/UsageStorage.php index 7e3c08e12a..88d0beca01 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageStorage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageStorage.php @@ -16,79 +16,23 @@ class UsageStorage extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('storage', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for the occupied storage size (in bytes).', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesCount', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of files.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('bucketsCount', [ + ->addRule('bucketsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of buckets.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('bucketsCreate', [ + ->addRule('filesTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for buckets created.', + 'description' => 'Aggregated stats for total number of files.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('bucketsRead', [ + ->addRule('filesStorage', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for buckets read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('bucketsUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for buckets updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('bucketsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for buckets deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('filesDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for files deleted.', + 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageUsers.php b/src/Appwrite/Utopia/Response/Model/UsageUsers.php index 4c7b37d50f..c0cc4baa54 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageUsers.php +++ b/src/Appwrite/Utopia/Response/Model/UsageUsers.php @@ -16,62 +16,21 @@ class UsageUsers extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('usersCount', [ + ->addRule('usersTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of users.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('usersCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for users created.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('usersRead', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for users read.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('usersUpdate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for users updated.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('usersDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for users deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('sessionsCreate', [ + + ->addRule('sessionsTotal', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for sessions created.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('sessionsProviderCreate', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('sessionsDelete', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for sessions deleted.', - 'default' => [], - 'example' => [], - 'array' => true - ]) ; } From 6d60b0bd8cc40b837947b42b9a920c734feeae24 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 3 Oct 2023 11:40:34 +0300 Subject: [PATCH 052/144] sync with 1.4 --- .env | 4 +- app/views/install/compose.phtml | 9 +- app/workers/audits.php | 62 -- app/workers/builds.php | 587 ----------- app/workers/certificates.php | 492 --------- app/workers/databases.php | 474 --------- app/workers/deletes.php | 965 ------------------ app/workers/functions.php | 426 -------- app/workers/mails.php | 106 -- app/workers/messaging.php | 90 -- app/workers/migrations.php | 308 ------ app/workers/webhooks.php | 98 -- composer.json | 2 +- src/Appwrite/Platform/Workers/Builds.php | 20 +- src/Appwrite/Platform/Workers/Deletes.php | 13 +- tests/e2e/Services/Avatars/AvatarsBase.php | 36 +- .../Functions/FunctionsCustomServerTest.php | 125 ++- tests/e2e/Services/GraphQL/AvatarsTest.php | 2 +- .../Health/HealthCustomServerTest.php | 157 ++- tests/e2e/Services/Users/UsersBase.php | 12 + tests/resources/docker/docker-compose.yml | 2 + tests/resources/initials.png | Bin 3112 -> 3075 bytes tests/unit/Event/EventTest.php | 25 +- 23 files changed, 314 insertions(+), 3701 deletions(-) delete mode 100644 app/workers/audits.php delete mode 100644 app/workers/builds.php delete mode 100644 app/workers/certificates.php delete mode 100644 app/workers/databases.php delete mode 100644 app/workers/deletes.php delete mode 100644 app/workers/functions.php delete mode 100644 app/workers/mails.php delete mode 100644 app/workers/messaging.php delete mode 100644 app/workers/migrations.php delete mode 100644 app/workers/webhooks.php diff --git a/.env b/.env index 189095e9e5..e6c1a68108 100644 --- a/.env +++ b/.env @@ -57,8 +57,8 @@ _APP_SMTP_PORT=1025 _APP_SMTP_SECURE= _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= -_APP_SMS_PROVIDER=sms://username:password@mock -_APP_SMS_FROM=+123456789 +_APP_SMS_PROVIDER= +_APP_SMS_FROM= _APP_STORAGE_LIMIT=30000000 _APP_STORAGE_PREVIEW_LIMIT=20000000 _APP_FUNCTIONS_SIZE_LIMIT=30000000 diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index d59f97b9da..ae287bd385 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -84,7 +84,9 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET @@ -196,6 +198,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -381,7 +384,9 @@ services: - _APP_FUNCTIONS_BUILD_TIMEOUT - _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_MEMORY + - _APP_FUNCTIONS_SIZE_LIMIT - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_DOMAIN - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY @@ -648,7 +653,7 @@ services: - _APP_DB_PASS appwrite-assistant: - image: appwrite/assistant:0.2.1 + image: appwrite/assistant:0.2.2 container_name: appwrite-assistant <<: *x-logging restart: unless-stopped @@ -662,7 +667,7 @@ services: hostname: appwrite-executor <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.4.1 + image: openruntimes/executor:0.4.2 networks: - appwrite - runtimes diff --git a/app/workers/audits.php b/app/workers/audits.php deleted file mode 100644 index 8369ec74ec..0000000000 --- a/app/workers/audits.php +++ /dev/null @@ -1,62 +0,0 @@ -args['event']; - $payload = $this->args['payload']; - $mode = $this->args['mode']; - $resource = $this->args['resource']; - $userAgent = $this->args['userAgent']; - $ip = $this->args['ip']; - - $user = new Document($this->args['user']); - $project = new Document($this->args['project']); - - $userName = $user->getAttribute('name', ''); - $userEmail = $user->getAttribute('email', ''); - - $dbForProject = $this->getProjectDB($project); - $audit = new Audit($dbForProject); - $audit->log( - userId: $user->getInternalId(), - // Pass first, most verbose event pattern - event: $event, - resource: $resource, - userAgent: $userAgent, - ip: $ip, - location: '', - data: [ - 'userId' => $user->getId(), - 'userName' => $userName, - 'userEmail' => $userEmail, - 'mode' => $mode, - 'data' => $payload, - ] - ); - } - - public function shutdown(): void - { - } -} diff --git a/app/workers/builds.php b/app/workers/builds.php deleted file mode 100644 index 89aa22edb7..0000000000 --- a/app/workers/builds.php +++ /dev/null @@ -1,587 +0,0 @@ -executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); - } - - public function run(): void - { - $type = $this->args['type'] ?? ''; - $project = new Document($this->args['project'] ?? []); - $resource = new Document($this->args['resource'] ?? []); - $deployment = new Document($this->args['deployment'] ?? []); - $template = new Document($this->args['template'] ?? []); - - switch ($type) { - case BUILD_TYPE_DEPLOYMENT: - case BUILD_TYPE_RETRY: - Console::info('Creating build for deployment: ' . $deployment->getId()); - $github = new GitHub($this->getCache()); - $this->buildDeployment($github, $project, $resource, $deployment, $template); - break; - - default: - throw new \Exception('Invalid build type'); - break; - } - } - - /** - * @throws \Utopia\Database\Exception\Authorization - * @throws \Utopia\Database\Exception\Structure - * @throws Throwable - */ - protected function buildDeployment(GitHub $github, Document $project, Document $function, Document $deployment, Document $template) - { - global $register; - - $dbForProject = $this->getProjectDB($project); - $dbForConsole = $this->getConsoleDB(); - - $function = $dbForProject->getDocument('functions', $function->getId()); - if ($function->isEmpty()) { - throw new Exception('Function not found', 404); - } - - $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); - if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404); - } - - if (empty($deployment->getAttribute('entrypoint', ''))) { - throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); - } - - $version = $function->getAttribute('version', 'v2'); - $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); - $key = $function->getAttribute('runtime'); - $runtime = isset($runtimes[$key]) ? $runtimes[$key] : null; - if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); - } - - // Realtime preparation - $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ - 'functionId' => $function->getId(), - 'deploymentId' => $deployment->getId() - ]); - - $startTime = DateTime::now(); - $durationStart = \microtime(true); - - $buildId = $deployment->getAttribute('buildId', ''); - - $isNewBuild = empty($buildId); - - if ($isNewBuild) { - $buildId = ID::unique(); - $build = $dbForProject->createDocument('builds', new Document([ - '$id' => $buildId, - '$permissions' => [], - 'startTime' => $startTime, - 'deploymentInternalId' => $deployment->getInternalId(), - 'deploymentId' => $deployment->getId(), - 'status' => 'processing', - 'path' => '', - 'runtime' => $function->getAttribute('runtime'), - 'source' => $deployment->getAttribute('path', ''), - 'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)), - 'logs' => '', - 'endTime' => null, - 'duration' => 0, - 'size' => 0 - ])); - - $deployment->setAttribute('buildId', $build->getId()); - $deployment->setAttribute('buildInternalId', $build->getInternalId()); - $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); - } else { - $build = $dbForProject->getDocument('builds', $buildId); - } - - $source = $deployment->getAttribute('path', ''); - $installationId = $deployment->getAttribute('installationId', ''); - $providerRepositoryId = $deployment->getAttribute('providerRepositoryId', ''); - $providerCommitHash = $deployment->getAttribute('providerCommitHash', ''); - $isVcsEnabled = $providerRepositoryId ? true : false; - $owner = ''; - $repositoryName = ''; - - if ($isVcsEnabled) { - $installation = $dbForConsole->getDocument('installations', $installationId); - $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); - - $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); - } - - try { - if ($isNewBuild && $isVcsEnabled) { - $tmpDirectory = '/tmp/builds/' . $buildId . '/code'; - $rootDirectory = $function->getAttribute('providerRootDirectory', ''); - $rootDirectory = \rtrim($rootDirectory, '/'); - $rootDirectory = \ltrim($rootDirectory, '.'); - $rootDirectory = \ltrim($rootDirectory, '/'); - - $owner = $github->getOwnerName($providerInstallationId); - $repositoryName = $github->getRepositoryName($providerRepositoryId); - - $cloneOwner = $deployment->getAttribute('providerRepositoryOwner', $owner); - $cloneRepository = $deployment->getAttribute('providerRepositoryName', $repositoryName); - - $branchName = $deployment->getAttribute('providerBranch'); - $commitHash = $deployment->getAttribute('providerCommitHash', ''); - $gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $branchName, $tmpDirectory, $rootDirectory, $commitHash); - $stdout = ''; - $stderr = ''; - Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr); - $exit = Console::execute($gitCloneCommand, '', $stdout, $stderr); - - if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $stderr); - } - - // Build from template - $templateRepositoryName = $template->getAttribute('repositoryName', ''); - $templateOwnerName = $template->getAttribute('ownerName', ''); - $templateBranch = $template->getAttribute('branch', ''); - - $templateRootDirectory = $template->getAttribute('rootDirectory', ''); - $templateRootDirectory = \rtrim($templateRootDirectory, '/'); - $templateRootDirectory = \ltrim($templateRootDirectory, '.'); - $templateRootDirectory = \ltrim($templateRootDirectory, '/'); - - if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) { - // Clone template repo - $tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '/template'; - $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $tmpTemplateDirectory, $templateRootDirectory); - $exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr); - - if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $stderr); - } - - // Ensure directories - Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr); - Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr); - - // Merge template into user repo - Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr); - - // Commit and push - $exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $stdout, $stderr); - - if ($exit !== 0) { - throw new \Exception('Unable to push code repository: ' . $stderr); - } - - $exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $stdout, $stderr); - - if ($exit !== 0) { - throw new \Exception('Unable to get vcs commit SHA: ' . $stderr); - } - - $providerCommitHash = \trim($stdout); - $authorUrl = "https://github.com/$cloneOwner"; - - $deployment->setAttribute('providerCommitHash', $providerCommitHash ?? ''); - $deployment->setAttribute('providerCommitAuthorUrl', $authorUrl); - $deployment->setAttribute('providerCommitAuthor', 'Appwrite'); - $deployment->setAttribute('providerCommitMessage', "Create '" . $function->getAttribute('name', '') . "' function"); - $deployment->setAttribute('providerCommitUrl', "https://github.com/$cloneOwner/$cloneRepository/commit/$providerCommitHash"); - $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); - - /** - * Send realtime Event - */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - } - - $tmpPath = '/tmp/builds/' . \escapeshellcmd($buildId); - $tmpPathFile = $tmpPath . '/code.tar.gz'; - - Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); - - $deviceFunctions = $this->getFunctionsDevice($project->getId()); - - $localDevice = new Local(); - $buffer = $localDevice->read($tmpPathFile); - $mimeType = $localDevice->getFileMimeType($tmpPathFile); - - $path = $deviceFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); - $result = $deviceFunctions->write($path, $buffer, $mimeType); - - if (!$result) { - throw new \Exception("Unable to move file"); - } - - Console::execute('rm -rf ' . $tmpPath, '', $stdout, $stderr); - - $source = $path; - - $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source)); - - $this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); - } - - /** Request the executor to build the code... */ - $build->setAttribute('status', 'building'); - $build = $dbForProject->updateDocument('builds', $buildId, $build); - - if ($isVcsEnabled) { - $this->runGitAction('building', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); - } - - /** Trigger Webhook */ - $deploymentModel = new Deployment(); - - $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); - $deploymentUpdate - ->setProject($project) - ->setEvent('functions.[functionId].deployments.[deploymentId].update') - ->setParam('functionId', $function->getId()) - ->setParam('deploymentId', $deployment->getId()) - ->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules()))) - ->trigger(); - - /** Trigger Functions */ - $pools = $register->get('pools'); - $connection = $pools->get('queue')->pop(); - - $functions = new Func($connection->getResource()); - $functions - ->from($deploymentUpdate) - ->trigger(); - - $connection->reclaim(); - - /** Trigger Realtime */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - - $vars = []; - - // Shared vars - foreach ($function->getAttribute('varsProject', []) as $var) { - $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); - } - - // Function vars - foreach ($function->getAttribute('vars', []) as $var) { - $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); - } - - // Appwrite vars - $vars = \array_merge($vars, [ - 'APPWRITE_FUNCTION_ID' => $function->getId(), - 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), - 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), - 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), - 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', - 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - ]); - - $command = $deployment->getAttribute('commands', ''); - $command = \str_replace('"', '\\"', $command); - - $response = null; - - $err = null; - - // TODO: Remove run() wrapper when switching to new utopia queue. That should be done on Swoole adapter in the libary - Co\run(function () use ($project, $deployment, &$response, $source, $function, $runtime, $vars, $command, &$build, $dbForProject, $allEvents, &$err) { - Co::join([ - Co\go(function () use (&$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) { - try { - $version = $function->getAttribute('version', 'v2'); - $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'; - - $response = $this->executor->createRuntime( - deploymentId: $deployment->getId(), - projectId: $project->getId(), - source: $source, - image: $runtime['image'], - version: $version, - remove: true, - entrypoint: $deployment->getAttribute('entrypoint'), - destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", - variables: $vars, - command: $command - ); - } catch (Exception $error) { - $err = $error; - } - }), - Co\go(function () use ($project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err) { - try { - $this->executor->getLogs( - deploymentId: $deployment->getId(), - projectId: $project->getId(), - callback: function ($logs) use (&$response, &$build, $dbForProject, $allEvents, $project) { - if ($response === null) { - $build = $dbForProject->getDocument('builds', $build->getId()); - - if ($build->isEmpty()) { - throw new Exception('Build not found', 404); - } - - $build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs); - $build = $dbForProject->updateDocument('builds', $build->getId(), $build); - - /** - * Send realtime Event - */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - } - } - ); - } catch (Exception $error) { - if (empty($err)) { - $err = $error; - } - } - }), - ]); - }); - - if ($err) { - throw $err; - } - - $endTime = DateTime::now(); - $durationEnd = \microtime(true); - - /** Update the build document */ - $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); - $build->setAttribute('endTime', $endTime); - $build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart))); - $build->setAttribute('status', 'ready'); - $build->setAttribute('path', $response['path']); - $build->setAttribute('size', $response['size']); - $build->setAttribute('logs', $response['output']); - - if ($isVcsEnabled) { - $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); - } - - Console::success("Build id: $buildId created"); - - /** Set auto deploy */ - if ($deployment->getAttribute('activate') === true) { - $function->setAttribute('deploymentInternalId', $deployment->getInternalId()); - $function->setAttribute('deployment', $deployment->getId()); - $function->setAttribute('live', true); - $function = $dbForProject->updateDocument('functions', $function->getId(), $function); - } - - /** Update function schedule */ - $dbForConsole = $this->getConsoleDB(); - // Inform scheduler if function is still active - $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule - ->setAttribute('resourceUpdatedAt', DateTime::now()) - ->setAttribute('schedule', $function->getAttribute('schedule')) - ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - } catch (\Throwable $th) { - $endTime = DateTime::now(); - $durationEnd = \microtime(true); - $build->setAttribute('endTime', $endTime); - $build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart))); - $build->setAttribute('status', 'failed'); - $build->setAttribute('logs', $th->getMessage()); - Console::error($th->getMessage()); - Console::error($th->getFile() . ':' . $th->getLine()); - Console::error($th->getTraceAsString()); - - if ($isVcsEnabled) { - $this->runGitAction('failed', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole); - } - } finally { - $build = $dbForProject->updateDocument('builds', $buildId, $build); - - /** - * Send realtime Event - */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - - /** Update usage stats */ - if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { - $statsd = $register->get('statsd'); - $usage = new Stats($statsd); - $usage - ->setParam('projectInternalId', $project->getInternalId()) - ->setParam('projectId', $project->getId()) - ->setParam('functionId', $function->getId()) - ->setParam('builds.{scope}.compute', 1) - ->setParam('buildStatus', $build->getAttribute('status', '')) - ->setParam('buildTime', $build->getAttribute('duration')) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0) - ->submit(); - } - } - } - - protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole): void - { - if ($function->getAttribute('providerSilentMode', false) === true) { - return; - } - - $deployment = $dbForProject->getDocument('deployments', $deploymentId); - $commentId = $deployment->getAttribute('providerCommentId', ''); - - if (!empty($providerCommitHash)) { - $message = match ($status) { - 'ready' => 'Build succeeded.', - 'failed' => 'Build failed.', - 'processing' => 'Building...', - default => $status - }; - - $state = match ($status) { - 'ready' => 'success', - 'failed' => 'failure', - 'processing' => 'pending', - default => $status - }; - - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - - $name = "{$functionName} ({$projectName})"; - - $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; - $hostname = App::getEnv('_APP_DOMAIN'); - $functionId = $function->getId(); - $projectId = $project->getId(); - $providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$projectId/functions/function-$functionId"; - - $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, $state, $message, $providerTargetUrl, $name); - } - - if (!empty($commentId)) { - $retries = 0; - - while (true) { - $retries++; - - try { - $dbForConsole->createDocument('vcsCommentLocks', new Document([ - '$id' => $commentId - ])); - break; - } catch (Exception $err) { - if ($retries >= 9) { - throw $err; - } - - \sleep(1); - } - } - - // Wrap in try/finally to ensure lock file gets deleted - try { - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); - $comment->addBuild($project, $function, $status, $deployment->getId(), ['type' => 'logs']); - $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); - } finally { - $dbForConsole->deleteDocument('vcsCommentLocks', $commentId); - } - } - } - - public function shutdown(): void - { - } -} diff --git a/app/workers/certificates.php b/app/workers/certificates.php deleted file mode 100644 index 4a3b9bccb5..0000000000 --- a/app/workers/certificates.php +++ /dev/null @@ -1,492 +0,0 @@ -dbForConsole = $this->getConsoleDB(); - - $skipCheck = $this->args['skipRenewCheck'] ?? false; // If true, we won't double-check expiry from cert file - $document = new Document($this->args['domain'] ?? []); - $domain = new Domain($document->getAttribute('domain', '')); - - // Get current certificate - $certificate = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]); - - // If we don't have certificate for domain yet, let's create new document. At the end we save it - if (!$certificate) { - $certificate = new Document(); - $certificate->setAttribute('domain', $domain->get()); - } - - $success = false; - - try { - // Email for alerts is required by LetsEncrypt - $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); - if (empty($email)) { - throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); - } - - // Validate domain and DNS records. Skip if job is forced - if (!$skipCheck) { - $mainDomain = $this->getMainDomain(); - $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; - $this->validateDomain($domain, $isMainDomain); - } - - // If certificate exists already, double-check expiry date. Skip if job is forced - if (!$skipCheck && !$this->isRenewRequired($domain->get())) { - throw new Exception('Renew isn\'t required.'); - } - - // Prepare folder name for certbot. Using this helps prevent miss-match in LetsEncrypt configuration when renewing certificate - $folder = ID::unique(); - - // Generate certificate files using Let's Encrypt - $letsEncryptData = $this->issueCertificate($folder, $domain->get(), $email); - - // Command succeeded, store all data into document - $logs = 'Certificate successfully generated.'; - $certificate->setAttribute('logs', \mb_strcut($logs, 0, 1000000));// Limit to 1MB - - // Give certificates to Traefik - $this->applyCertificateFiles($folder, $domain->get(), $letsEncryptData); - - // Update certificate info stored in database - $certificate->setAttribute('renewDate', $this->getRenewDate($domain->get())); - $certificate->setAttribute('attempts', 0); - $certificate->setAttribute('issueDate', DateTime::now()); - - $success = true; - } catch (Throwable $e) { - $logs = $e->getMessage(); - - // Set exception as log in certificate document - $certificate->setAttribute('logs', \mb_strcut($logs, 0, 1000000));// Limit to 1MB - - // Increase attempts count - $attempts = $certificate->getAttribute('attempts', 0) + 1; - $certificate->setAttribute('attempts', $attempts); - - // Store cuttent time as renew date to ensure another attempt in next maintenance cycle - $certificate->setAttribute('renewDate', DateTime::now()); - - // Send email to security email - $this->notifyError($domain->get(), $e->getMessage(), $attempts); - } finally { - // All actions result in new updatedAt date - $certificate->setAttribute('updated', DateTime::now()); - - // Save all changes we made to certificate document into database - $this->saveCertificateDocument($domain->get(), $certificate, $success); - } - } - - public function shutdown(): void - { - } - - /** - * Save certificate data into database. - * - * @param string $domain Domain name that certificate is for - * @param Document $certificate Certificate document that we need to save - * @param bool $success Was certificate generation successful? - * - * @return void - */ - private function saveCertificateDocument(string $domain, Document $certificate, bool $success): void - { - // Check if update or insert required - $certificateDocument = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]); - if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) { - // Merge new data with current data - $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); - - $certificate = $this->dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); - } else { - $certificate->removeAttribute('$internalId'); - $certificate = $this->dbForConsole->createDocument('certificates', $certificate); - } - - $certificateId = $certificate->getId(); - $this->updateDomainDocuments($certificateId, $domain, $success); - } - - /** - * Get main domain. Needed as we do different checks for main and non-main domains. - * - * @return null|string Returns main domain. If null, there is no main domain yet. - */ - private function getMainDomain(): ?string - { - $envDomain = App::getEnv('_APP_DOMAIN', ''); - if (!empty($envDomain) && $envDomain !== 'localhost') { - return $envDomain; - } - - return null; - } - - /** - * Internal domain validation functionality to prevent unnecessary attempts failed from Let's Encrypt side. We check: - * - Domain needs to be public and valid (prevents NFT domains that are not supported by Let's Encrypt) - * - Domain must have proper DNS record - * - * @param Domain $domain Domain which we validate - * @param bool $isMainDomain In case of master domain, we look for different DNS configurations - * - * @return void - */ - private function validateDomain(Domain $domain, bool $isMainDomain): void - { - if (empty($domain->get())) { - throw new Exception('Missing certificate domain.'); - } - - if (!$domain->isKnown() || $domain->isTest()) { - throw new Exception('Unknown public suffix for domain.'); - } - - if (!$isMainDomain) { - // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? - - // Validate if domain target is properly configured - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); - - if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); - } - - // Verify domain with DNS records - $validator = new CNAME($target->get()); - if (!$validator->isValid($domain->get())) { - throw new Exception('Failed to verify domain DNS records.'); - } - } else { - // Main domain validation - // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? - } - } - - /** - * Reads expiry date of certificate from file and decides if renewal is required or not. - * - * @param string $domain Domain for which we check certificate file - * - * @return bool True, if certificate needs to be renewed - */ - private function isRenewRequired(string $domain): bool - { - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; - if (\file_exists($certPath)) { - $validTo = null; - - $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? 0; - - if (empty($validTo)) { - throw new Exception('Unable to read certificate file (cert.pem).'); - } - - // LetsEncrypt allows renewal 30 days before expiry - $expiryInAdvance = (60 * 60 * 24 * 30); - if ($validTo - $expiryInAdvance > \time()) { - return false; - } - } - - return true; - } - - /** - * LetsEncrypt communication to issue certificate (using certbot CLI) - * - * @param string $folder Folder into which certificates should be generated - * @param string $domain Domain to generate certificate for - * - * @return array Named array with keys 'stdout' and 'stderr', both string - */ - private function issueCertificate(string $folder, string $domain, string $email): array - { - $stdout = ''; - $stderr = ''; - - $staging = (App::isProduction()) ? '' : ' --dry-run'; - $exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}" - . " --email " . $email - . " --cert-name " . $folder - . " -w " . APP_STORAGE_CERTIFICATES - . " -d {$domain}", '', $stdout, $stderr); - - // Unexpected error, usually 5XX, API limits, ... - if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $stderr); - } - - return [ - 'stdout' => $stdout, - 'stderr' => $stderr - ]; - } - - /** - * Read new renew date from certificate file generated by Let's Encrypt - * - * @param string $domain Domain which certificate was generated for - * - * @return string - */ - private function getRenewDate(string $domain): string - { - $certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem'; - $certData = openssl_x509_parse(file_get_contents($certPath)); - $validTo = $certData['validTo_time_t'] ?? null; - $dt = (new \DateTime())->setTimestamp($validTo); - return DateTime::addSeconds($dt, -60 * 60 * 24 * 30); // -30 days - } - - /** - * Method to take files from Let's Encrypt, and put it into Traefik. - * - * @param string $domain Domain which certificate was generated for - * @param string $folder Folder in which certificates were generated - * @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error - * - * @return void - */ - private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void - { - // Prepare folder in storage for domain - $path = APP_STORAGE_CERTIFICATES . '/' . $domain; - if (!\is_readable($path)) { - if (!\mkdir($path, 0755, true)) { - throw new Exception('Failed to create path for certificate.'); - } - } - - // Move generated files - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); - } - - $config = \implode(PHP_EOL, [ - "tls:", - " certificates:", - " - certFile: /storage/certificates/{$domain}/fullchain.pem", - " keyFile: /storage/certificates/{$domain}/privkey.pem" - ]); - - // Save configuration into Traefik using our new cert files - if (!\file_put_contents(APP_STORAGE_CONFIG . '/' . $domain . '.yml', $config)) { - throw new Exception('Failed to save Traefik configuration.'); - } - } - - /** - * Method to make sure information about error is delivered to admnistrator. - * - * @param string $domain Domain that caused the error - * @param string $errorMessage Verbose error message - * @param int $attempt How many times it failed already - * - * @return void - */ - private function notifyError(string $domain, string $errorMessage, int $attempt): void - { - // Log error into console - Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); - - // Send mail to administratore mail - - $locale = new Locale(App::getEnv('_APP_LOCALE', 'en')); - if (!$locale->getText('emails.sender') || !$locale->getText("emails.certificate.hello") || !$locale->getText("emails.certificate.subject") || !$locale->getText("emails.certificate.body") || !$locale->getText("emails.certificate.footer") || !$locale->getText("emails.certificate.thanks") || !$locale->getText("emails.certificate.signature")) { - $locale->setDefault('en'); - } - - $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); - - $subject = \sprintf($locale->getText("emails.certificate.subject"), $domain); - $body->setParam('{{domain}}', $domain); - $body->setParam('{{error}}', $errorMessage); - $body->setParam('{{attempt}}', $attempt); - - $body - ->setParam('{{subject}}', $subject) - ->setParam('{{hello}}', $locale->getText("emails.certificate.hello")) - ->setParam('{{body}}', $locale->getText("emails.certificate.body")) - ->setParam('{{redirect}}', 'https://' . $domain) - ->setParam('{{footer}}', $locale->getText("emails.certificate.footer")) - ->setParam('{{thanks}}', $locale->getText("emails.certificate.thanks")) - ->setParam('{{signature}}', $locale->getText("emails.certificate.signature")) - ->setParam('{{project}}', 'Console') - ->setParam('{{direction}}', $locale->getText('settings.direction')) - ->setParam('{{bg-body}}', '#f7f7f7') - ->setParam('{{bg-content}}', '#ffffff') - ->setParam('{{text-content}}', '#000000'); - - $body = $body->render(); - $mail = new Mail(); - $mail - ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) - ->setName('Appwrite Administrator') - ->trigger(); - } - - /** - * Update all existing domain documents so they have relation to correct certificate document. - * This solved issues: - * - when adding a domain for which there is already a certificate - * - when renew creates new document? It might? - * - overall makes it more reliable - * - * @param string $certificateId ID of a new or updated certificate document - * @param string $domain Domain that is affected by new certificate - * @param bool $success Was certificate generation successful? - * - * @return void - */ - private function updateDomainDocuments(string $certificateId, string $domain, bool $success): void - { - $rule = $this->dbForConsole->findOne('rules', [ - Query::equal('domain', [$domain]), - ]); - - if ($rule !== false && !$rule->isEmpty()) { - $rule->setAttribute('certificateId', $certificateId); - $rule->setAttribute('status', $success ? 'verified' : 'unverified'); - $this->dbForConsole->updateDocument('rules', $rule->getId(), $rule); - - $projectId = $rule->getAttribute('projectId'); - - // Skip events for console project (triggered by auto-ssl generation for 1 click setups) - if ($projectId === 'console') { - return; - } - - $project = $this->dbForConsole->getDocument('projects', $projectId); - - /** Trigger Webhook */ - $ruleModel = new Rule(); - $ruleUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); - $ruleUpdate - ->setProject($project) - ->setEvent('rules.[ruleId].update') - ->setParam('ruleId', $rule->getId()) - ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) - ->trigger(); - - /** Trigger Functions */ - $ruleUpdate - ->setClass(Event::FUNCTIONS_CLASS_NAME) - ->setQueue(Event::FUNCTIONS_QUEUE_NAME) - ->trigger(); - - /** Trigger realtime event */ - $allEvents = Event::generateEvents('rules.[ruleId].update', [ - 'ruleId' => $rule->getId(), - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $rule, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - } - } -} diff --git a/app/workers/databases.php b/app/workers/databases.php deleted file mode 100644 index 764f668f33..0000000000 --- a/app/workers/databases.php +++ /dev/null @@ -1,474 +0,0 @@ -args['type']; - $project = new Document($this->args['project']); - $collection = new Document($this->args['collection'] ?? []); - $document = new Document($this->args['document'] ?? []); - $database = new Document($this->args['database'] ?? []); - - if ($collection->isEmpty()) { - throw new DatabaseException('Missing collection'); - } - - if ($document->isEmpty()) { - throw new DatabaseException('Missing document'); - } - - switch (strval($type)) { - case DATABASE_TYPE_CREATE_ATTRIBUTE: - $this->createAttribute($database, $collection, $document, $project); - break; - case DATABASE_TYPE_DELETE_ATTRIBUTE: - $this->deleteAttribute($database, $collection, $document, $project); - break; - case DATABASE_TYPE_CREATE_INDEX: - $this->createIndex($database, $collection, $document, $project); - break; - case DATABASE_TYPE_DELETE_INDEX: - $this->deleteIndex($database, $collection, $document, $project); - break; - - default: - Console::error('No database operation for type: ' . $type); - break; - } - } - - public function shutdown(): void - { - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $attribute - * @param Document $project - */ - protected function createAttribute(Document $database, Document $collection, Document $attribute, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); - /** - * Fetch attribute from the database, since with Resque float values are loosing informations. - */ - $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); - - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $type = $attribute->getAttribute('type', ''); - $size = $attribute->getAttribute('size', 0); - $required = $attribute->getAttribute('required', false); - $default = $attribute->getAttribute('default', null); - $signed = $attribute->getAttribute('signed', true); - $array = $attribute->getAttribute('array', false); - $format = $attribute->getAttribute('format', ''); - $formatOptions = $attribute->getAttribute('formatOptions', []); - $filters = $attribute->getAttribute('filters', []); - $options = $attribute->getAttribute('options', []); - $project = $dbForConsole->getDocument('projects', $projectId); - - try { - switch ($type) { - case Database::VAR_RELATIONSHIP: - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { - throw new DatabaseException('Collection not found'); - } - - if ( - !$dbForProject->createRelationship( - collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), - type: $options['relationType'], - twoWay: $options['twoWay'], - id: $key, - twoWayKey: $options['twoWayKey'], - onDelete: $options['onDelete'], - ) - ) { - throw new DatabaseException('Failed to create Attribute'); - } - - if ($options['twoWay']) { - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available')); - } - break; - default: - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); - } - } - - $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); - } catch (\Exception $e) { - Console::error($e->getMessage()); - - if ($e instanceof DatabaseException) { - $attribute->setAttribute('error', $e->getMessage()); - if (isset($relatedAttribute)) { - $relatedAttribute->setAttribute('error', $e->getMessage()); - } - } - - $dbForProject->updateDocument( - 'attributes', - $attribute->getId(), - $attribute->setAttribute('status', 'failed') - ); - - if (isset($relatedAttribute)) { - $dbForProject->updateDocument( - 'attributes', - $relatedAttribute->getId(), - $relatedAttribute->setAttribute('status', 'failed') - ); - } - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project, - ); - - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $attribute - * @param Document $project - * @throws Throwable - */ - protected function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); - $collectionId = $collection->getId(); - $key = $attribute->getAttribute('key', ''); - $status = $attribute->getAttribute('status', ''); - $type = $attribute->getAttribute('type', ''); - $project = $dbForConsole->getDocument('projects', $projectId); - $options = $attribute->getAttribute('options', []); - $relatedAttribute = new Document(); - $relatedCollection = new Document(); - // possible states at this point: - // - available: should not land in queue; controller flips these to 'deleting' - // - processing: hasn't finished creating - // - deleting: was available, in deletion queue for first time - // - failed: attribute was never created - // - stuck: attribute was available but cannot be removed - - try { - if ($status !== 'failed') { - if ($type === Database::VAR_RELATIONSHIP) { - if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - if ($relatedCollection->isEmpty()) { - throw new DatabaseException('Collection not found'); - } - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); - } - - if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); - throw new DatabaseException('Failed to delete Relationship'); - } - } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new DatabaseException('Failed to delete Attribute'); - } - } - - $dbForProject->deleteDocument('attributes', $attribute->getId()); - - if (!$relatedAttribute->isEmpty()) { - $dbForProject->deleteDocument('attributes', $relatedAttribute->getId()); - } - } catch (\Exception $e) { - Console::error($e->getMessage()); - - if ($e instanceof DatabaseException) { - $attribute->setAttribute('error', $e->getMessage()); - if (!$relatedAttribute->isEmpty()) { - $relatedAttribute->setAttribute('error', $e->getMessage()); - } - } - $dbForProject->updateDocument( - 'attributes', - $attribute->getId(), - $attribute->setAttribute('status', 'stuck') - ); - if (!$relatedAttribute->isEmpty()) { - $dbForProject->updateDocument( - 'attributes', - $relatedAttribute->getId(), - $relatedAttribute->setAttribute('status', 'stuck') - ); - } - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - // The underlying database removes/rebuilds indexes when attribute is removed - // Update indexes table with changes - /** @var Document[] $indexes */ - $indexes = $collection->getAttribute('indexes', []); - - foreach ($indexes as $index) { - /** @var string[] $attributes */ - $attributes = $index->getAttribute('attributes'); - $lengths = $index->getAttribute('lengths'); - $orders = $index->getAttribute('orders'); - - $found = \array_search($key, $attributes); - - if ($found !== false) { - // If found, remove entry from attributes, lengths, and orders - // array_values wraps array_diff to reindex array keys - // when found attribute is removed from array - $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); - $lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : [])); - $orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : [])); - - if (empty($attributes)) { - $dbForProject->deleteDocument('indexes', $index->getId()); - } else { - $index - ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) - ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) - ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN); - - // Check if an index exists with the same attributes and orders - $exists = false; - foreach ($indexes as $existing) { - if ( - $existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself - && $existing->getAttribute('attributes') === $index->getAttribute('attributes') - && $existing->getAttribute('orders') === $index->getAttribute('orders') - ) { - $exists = true; - break; - } - } - - if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project); - } else { - $dbForProject->updateDocument('indexes', $index->getId(), $index); - } - } - } - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - - if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) { - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); - $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); - } - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $index - * @param Document $project - * @throws \Exception - */ - protected function createIndex(Document $database, Document $collection, Document $index, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); - $collectionId = $collection->getId(); - $key = $index->getAttribute('key', ''); - $type = $index->getAttribute('type', ''); - $attributes = $index->getAttribute('attributes', []); - $lengths = $index->getAttribute('lengths', []); - $orders = $index->getAttribute('orders', []); - $project = $dbForConsole->getDocument('projects', $projectId); - - try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { - throw new DatabaseException('Failed to create Index'); - } - $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); - } catch (\Exception $e) { - Console::error($e->getMessage()); - - if ($e instanceof DatabaseException) { - $index->setAttribute('error', $e->getMessage()); - } - $dbForProject->updateDocument( - 'indexes', - $index->getId(), - $index->setAttribute('status', 'failed') - ); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); - } - - /** - * @param Document $database - * @param Document $collection - * @param Document $index - * @param Document $project - */ - protected function deleteIndex(Document $database, Document $collection, Document $index, Document $project): void - { - $projectId = $project->getId(); - $dbForConsole = $this->getConsoleDB(); - $dbForProject = $this->getProjectDB($project); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); - $key = $index->getAttribute('key'); - $status = $index->getAttribute('status', ''); - $project = $dbForConsole->getDocument('projects', $projectId); - - try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { - throw new DatabaseException('Failed to delete index'); - } - $dbForProject->deleteDocument('indexes', $index->getId()); - } catch (\Exception $e) { - Console::error($e->getMessage()); - - if ($e instanceof DatabaseException) { - $index->setAttribute('error', $e->getMessage()); - } - $dbForProject->updateDocument( - 'indexes', - $index->getId(), - $index->setAttribute('status', 'stuck') - ); - } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); - } - - $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); - } -} diff --git a/app/workers/deletes.php b/app/workers/deletes.php deleted file mode 100644 index f3968c07f2..0000000000 --- a/app/workers/deletes.php +++ /dev/null @@ -1,965 +0,0 @@ -args['project'] ?? []); - $type = $this->args['type'] ?? ''; - switch (strval($type)) { - case DELETE_TYPE_DOCUMENT: - $document = new Document($this->args['document'] ?? []); - - switch ($document->getCollection()) { - case DELETE_TYPE_DATABASES: - $this->deleteDatabase($document, $project); - break; - case DELETE_TYPE_COLLECTIONS: - $this->deleteCollection($document, $project); - break; - case DELETE_TYPE_PROJECTS: - $this->deleteProject($document); - break; - case DELETE_TYPE_FUNCTIONS: - $this->deleteFunction($document, $project); - break; - case DELETE_TYPE_DEPLOYMENTS: - $this->deleteDeployment($document, $project); - break; - case DELETE_TYPE_USERS: - $this->deleteUser($document, $project); - break; - case DELETE_TYPE_TEAMS: - $this->deleteMemberships($document, $project); - if ($project->getId() === 'console') { - $this->deleteProjectsByTeam($document); - } - break; - case DELETE_TYPE_BUCKETS: - $this->deleteBucket($document, $project); - break; - case DELETE_TYPE_INSTALLATIONS: - $this->deleteInstallation($document, $project); - break; - case DELETE_TYPE_RULES: - $this->deleteRule($document, $project); - break; - default: - if (\str_starts_with($document->getCollection(), 'database_')) { - $this->deleteCollection($document, $project); - break; - } - Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); - break; - } - break; - - case DELETE_TYPE_EXECUTIONS: - $this->deleteExecutionLogs($this->args['datetime']); - break; - - case DELETE_TYPE_AUDIT: - $datetime = $this->args['datetime'] ?? null; - if (!empty($datetime)) { - $this->deleteAuditLogs($datetime); - } - - $document = new Document($this->args['document'] ?? []); - - if (!$document->isEmpty()) { - $this->deleteAuditLogsByResource('document/' . $document->getId(), $project); - } - - break; - - case DELETE_TYPE_ABUSE: - $this->deleteAbuseLogs($this->args['datetime']); - break; - - case DELETE_TYPE_REALTIME: - $this->deleteRealtimeUsage($this->args['datetime']); - break; - - case DELETE_TYPE_SESSIONS: - $this->deleteExpiredSessions(); - break; - - case DELETE_TYPE_USAGE: - $this->deleteUsageStats($this->args['hourlyUsageRetentionDatetime']); - break; - - case DELETE_TYPE_CACHE_BY_RESOURCE: - $this->deleteCacheByResource($project, $this->args['resource']); - break; - case DELETE_TYPE_CACHE_BY_TIMESTAMP: - $this->deleteCacheByDate($this->args['datetime']); - break; - case DELETE_TYPE_SCHEDULES: - $this->deleteSchedules($this->args['datetime']); - break; - default: - Console::error('No delete operation for type: ' . $type); - break; - } - } - - public function shutdown(): void - { - } - - /** - * @throws Exception - */ - protected function deleteSchedules(string $datetime): void - { - $this->listByGroup( - 'schedules', - [ - Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), - Query::equal('resourceType', ['function']), - Query::lessThanEqual('resourceUpdatedAt', $datetime), - Query::equal('active', [false]), - ], - $this->getConsoleDB(), - function (Document $document) { - $project = $this->getConsoleDB()->getDocument('projects', $document->getAttribute('projectId')); - - if ($project->isEmpty()) { - $this->getConsoleDB()->deleteDocument('schedules', $document->getId()); - Console::success('Deleted schedule for deleted project ' . $document->getAttribute('projectId')); - return; - } - - $function = $this->getProjectDB($project)->getDocument('functions', $document->getAttribute('resourceId')); - - if ($function->isEmpty()) { - $this->getConsoleDB()->deleteDocument('schedules', $document->getId()); - Console::success('Deleted schedule for function ' . $document->getAttribute('resourceId')); - } - } - ); - } - - /** - * @param Document $project - * @param string $resource - * @throws Exception - */ - protected function deleteCacheByResource(Document $project, string $resource): void - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]); - - if ($document) { - $cache = new Cache( - new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) - ); - - $this->deleteById( - $document, - $dbForProject, - function ($document) use ($cache, $projectId) { - $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); - - if ($cache->purge($document->getId())) { - Console::success('Deleting cache file: ' . $path); - } else { - Console::error('Failed to delete cache file: ' . $path); - } - } - ); - } - } - - /** - * @param string $datetime - * @throws Exception - */ - protected function deleteCacheByDate(string $datetime): void - { - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $cache = new Cache( - new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) - ); - - $query = [ - Query::lessThan('accessedAt', $datetime), - ]; - - $this->deleteByGroup( - 'cache', - $query, - $dbForProject, - function (Document $document) use ($cache, $projectId) { - $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); - - if ($cache->purge($document->getId())) { - Console::success('Deleting cache file: ' . $path); - } else { - Console::error('Failed to delete cache file: ' . $path); - } - } - ); - }); - } - - - /** - * @param Document $document database document - * @param Document $project - */ - protected function deleteDatabase(Document $document, Document $project): void - { - $databaseId = $document->getId(); - $projectId = $project->getId(); - - $dbForProject = $this->getProjectDB($project); - - $this->deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($project) { - $this->deleteCollection($document, $project); - }); - - $dbForProject->deleteCollection('database_' . $document->getInternalId()); - - $this->deleteAuditLogsByResource('database/' . $databaseId, $project); - } - - /** - * @param Document $document teams document - * @param Document $project - */ - protected function deleteCollection(Document $document, Document $project): void - { - $collectionId = $document->getId(); - $collectionInternalId = $document->getInternalId(); - $databaseId = $document->getAttribute('databaseId'); - $databaseInternalId = $document->getAttribute('databaseInternalId'); - - $dbForProject = $this->getProjectDB($project); - - $relationships = \array_filter( - $document->getAttribute('attributes'), - fn ($attribute) => $attribute['type'] === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - if (!$relationship['twoWay']) { - continue; - } - $relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']); - $dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']); - $dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId()); - $dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId()); - } - - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); - - $this->deleteByGroup('attributes', [ - Query::equal('databaseInternalId', [$databaseInternalId]), - Query::equal('collectionInternalId', [$collectionInternalId]) - ], $dbForProject); - - $this->deleteByGroup('indexes', [ - Query::equal('databaseInternalId', [$databaseInternalId]), - Query::equal('collectionInternalId', [$collectionInternalId]) - ], $dbForProject); - - $this->deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project); - } - - /** - * @param string $hourlyUsageRetentionDatetime - * @throws Exception - */ - protected function deleteUsageStats(string $hourlyUsageRetentionDatetime) - { - $this->deleteForProjectIds(function (Document $project) use ($hourlyUsageRetentionDatetime) { - $dbForProject = $this->getProjectDB($project); - // Delete Usage stats - $this->deleteByGroup('stats', [ - Query::lessThan('time', $hourlyUsageRetentionDatetime), - Query::equal('period', ['1h']), - ], $dbForProject); - }); - } - - /** - * @param Document $document teams document - * @param Document $project - */ - protected function deleteMemberships(Document $document, Document $project): void - { - $dbForProject = $this->getProjectDB($project); - $teamInternalId = $document->getInternalId(); - - // Delete Memberships - $this->deleteByGroup( - 'memberships', - [ - Query::equal('teamInternalId', [$teamInternalId]) - ], - $dbForProject, - function (Document $membership) use ($dbForProject) { - $userId = $membership->getAttribute('userId'); - $dbForProject->deleteCachedDocument('users', $userId); - } - ); - } - - /** - * @param \Utopia\Database\Document $document - * @return void - * @throws \Exception - */ - protected function deleteProjectsByTeam(Document $document): void - { - $dbForConsole = $this->getConsoleDB(); - - $projects = $dbForConsole->find('projects', [ - Query::equal('teamInternalId', [$document->getInternalId()]) - ]); - - foreach ($projects as $project) { - $this->deleteProject($project); - $dbForConsole->deleteDocument('projects', $project->getId()); - } - } - - /** - * @param Document $document project document - * @throws Exception - */ - protected function deleteProject(Document $document): void - { - $projectId = $document->getId(); - $projectInternalId = $document->getInternalId(); - - // Delete project certificates - $dbForConsole = $this->getConsoleDB(); - - $domains = $dbForConsole->find('domains', [ - Query::equal('projectInternalId', [$projectInternalId]) - ]); - - foreach ($domains as $domain) { - $this->deleteCertificates($domain); - } - - // Delete project tables - $dbForProject = $this->getProjectDB($document); - - while (true) { - $collections = $dbForProject->listCollections(); - - if (empty($collections)) { - break; - } - - foreach ($collections as $collection) { - $dbForProject->deleteCollection($collection->getId()); - } - } - - // Delete Platforms - $this->deleteByGroup('platforms', [ - Query::equal('projectInternalId', [$projectInternalId]) - ], $dbForConsole); - - // Delete Domains - $this->deleteByGroup('domains', [ - Query::equal('projectInternalId', [$projectInternalId]) - ], $dbForConsole); - - // Delete Keys - $this->deleteByGroup('keys', [ - Query::equal('projectInternalId', [$projectInternalId]) - ], $dbForConsole); - - // Delete Webhooks - $this->deleteByGroup('webhooks', [ - Query::equal('projectInternalId', [$projectInternalId]) - ], $dbForConsole); - - // Delete metadata tables - try { - $dbForProject->deleteCollection('_metadata'); - } catch (Exception) { - // Ignore: deleteCollection tries to delete a metadata entry after the collection is deleted, - // which will throw an exception here because the metadata collection is already deleted. - } - - // Delete all storage directories - $uploads = $this->getFilesDevice($projectId); - $functions = $this->getFunctionsDevice($projectId); - $builds = $this->getBuildsDevice($projectId); - $cache = $this->getCacheDevice($projectId); - - $uploads->delete($uploads->getRoot(), true); - $functions->delete($functions->getRoot(), true); - $builds->delete($builds->getRoot(), true); - $cache->delete($cache->getRoot(), true); - } - - /** - * @param Document $document user document - * @param Document $project - */ - protected function deleteUser(Document $document, Document $project): void - { - $userId = $document->getId(); - $userInternalId = $document->getInternalId(); - - $dbForProject = $this->getProjectDB($project); - - // Delete all sessions of this user from the sessions table and update the sessions field of the user record - $this->deleteByGroup('sessions', [ - Query::equal('userInternalId', [$userInternalId]) - ], $dbForProject); - - $dbForProject->deleteCachedDocument('users', $userId); - - // Delete Memberships and decrement team membership counts - $this->deleteByGroup('memberships', [ - Query::equal('userInternalId', [$userInternalId]) - ], $dbForProject, function (Document $document) use ($dbForProject) { - if ($document->getAttribute('confirm')) { // Count only confirmed members - $teamId = $document->getAttribute('teamId'); - $team = $dbForProject->getDocument('teams', $teamId); - if (!$team->isEmpty()) { - $team = $dbForProject->updateDocument( - 'teams', - $teamId, - // Ensure that total >= 0 - $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)) - ); - } - } - }); - - // Delete tokens - $this->deleteByGroup('tokens', [ - Query::equal('userInternalId', [$userInternalId]) - ], $dbForProject); - - // Delete identities - $this->deleteByGroup('identities', [ - Query::equal('userInternalId', [$userInternalId]) - ], $dbForProject); - } - - /** - * @param string $datetime - * @throws Exception - */ - protected function deleteExecutionLogs(string $datetime): void - { - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $dbForProject = $this->getProjectDB($project); - // Delete Executions - $this->deleteByGroup('executions', [ - Query::lessThan('$createdAt', $datetime) - ], $dbForProject); - }); - } - - protected function deleteExpiredSessions(): void - { - $consoleDB = $this->getConsoleDB(); - - $this->deleteForProjectIds(function (Document $project) use ($consoleDB) { - $dbForProject = $this->getProjectDB($project); - - $project = $consoleDB->getDocument('projects', $project->getId()); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); - - // Delete Sessions - $this->deleteByGroup('sessions', [ - Query::lessThan('$createdAt', $expired) - ], $dbForProject); - }); - } - - /** - * @param string $datetime - * @throws Exception - */ - protected function deleteRealtimeUsage(string $datetime): void - { - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $dbForProject = $this->getProjectDB($project); - // Delete Dead Realtime Logs - $this->deleteByGroup('realtime', [ - Query::lessThan('timestamp', $datetime) - ], $dbForProject); - }); - } - - /** - * @param string $datetime - * @throws Exception - */ - protected function deleteAbuseLogs(string $datetime): void - { - if (empty($datetime)) { - throw new Exception('Failed to delete audit logs. No datetime provided'); - } - - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $timeLimit = new TimeLimit("", 0, 1, $dbForProject); - $abuse = new Abuse($timeLimit); - $status = $abuse->cleanup($datetime); - if (!$status) { - throw new Exception('Failed to delete Abuse logs for project ' . $projectId); - } - }); - } - - /** - * @param string $datetime - * @throws Exception - */ - protected function deleteAuditLogs(string $datetime): void - { - if (empty($datetime)) { - throw new Exception('Failed to delete audit logs. No datetime provided'); - } - - $this->deleteForProjectIds(function (Document $project) use ($datetime) { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $audit = new Audit($dbForProject); - $status = $audit->cleanup($datetime); - if (!$status) { - throw new Exception('Failed to delete Audit logs for project' . $projectId); - } - }); - } - - /** - * @param string $resource - * @param Document $project - */ - protected function deleteAuditLogsByResource(string $resource, Document $project): void - { - $dbForProject = $this->getProjectDB($project); - - $this->deleteByGroup(Audit::COLLECTION, [ - Query::equal('resource', [$resource]) - ], $dbForProject); - } - - /** - * @param Document $document function document - * @param Document $project - */ - protected function deleteFunction(Document $document, Document $project): void - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $dbForConsole = $this->getConsoleDB(); - $functionId = $document->getId(); - $functionInternalId = $document->getInternalId(); - - /** - * Delete rules - */ - Console::info("Deleting rules for function " . $functionId); - $this->deleteByGroup('rules', [ - Query::equal('resourceType', ['function']), - Query::equal('resourceInternalId', [$functionInternalId]), - Query::equal('projectInternalId', [$project->getInternalId()]) - ], $dbForConsole, function (Document $document) use ($project) { - $this->deleteRule($document, $project); - }); - - /** - * Delete Variables - */ - Console::info("Deleting variables for function " . $functionId); - $this->deleteByGroup('variables', [ - Query::equal('resourceType', ['function']), - Query::equal('resourceInternalId', [$functionInternalId]) - ], $dbForProject); - - /** - * Delete Deployments - */ - Console::info("Deleting deployments for function " . $functionId); - $storageFunctions = $this->getFunctionsDevice($projectId); - $deploymentInternalIds = []; - $this->deleteByGroup('deployments', [ - Query::equal('resourceInternalId', [$functionInternalId]) - ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentInternalIds) { - $deploymentInternalIds[] = $document->getInternalId(); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } - }); - - /** - * Delete builds - */ - Console::info("Deleting builds for function " . $functionId); - $storageBuilds = $this->getBuildsDevice($projectId); - foreach ($deploymentInternalIds as $deploymentInternalId) { - $this->deleteByGroup('builds', [ - Query::equal('deploymentInternalId', [$deploymentInternalId]) - ], $dbForProject, function (Document $document) use ($storageBuilds) { - if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); - } - }); - } - - /** - * Delete Executions - */ - Console::info("Deleting executions for function " . $functionId); - $this->deleteByGroup('executions', [ - Query::equal('functionInternalId', [$functionInternalId]) - ], $dbForProject); - - /** - * Request executor to delete all deployment containers - */ - Console::info("Requesting executor to delete all deployment containers for function " . $functionId); - $this->deleteRuntimes($document, $project); - } - - /** - * @param Document $document deployment document - * @param Document $project - */ - protected function deleteDeployment(Document $document, Document $project): void - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $deploymentId = $document->getId(); - $deploymentInternalId = $document->getInternalId(); - - /** - * Delete deployment files - */ - Console::info("Deleting deployment files for deployment " . $deploymentId); - $storageFunctions = $this->getFunctionsDevice($projectId); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } - - /** - * Delete builds - */ - Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $this->getBuildsDevice($projectId); - $this->deleteByGroup('builds', [ - Query::equal('deploymentInternalId', [$deploymentInternalId]) - ], $dbForProject, function (Document $document) use ($storageBuilds) { - if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); - } - }); - - - /** - * Request executor to delete all deployment containers - */ - Console::info("Requesting executor to delete deployment container for deployment " . $deploymentId); - $this->deleteRuntimes($document, $project); - } - - - /** - * @param Document $document to be deleted - * @param Database $database to delete it from - * @param callable|null $callback to perform after document is deleted - * - * @return bool - * @throws \Utopia\Database\Exception\Authorization - */ - protected function deleteById(Document $document, Database $database, callable $callback = null): bool - { - if ($database->deleteDocument($document->getCollection(), $document->getId())) { - Console::success('Deleted document "' . $document->getId() . '" successfully'); - - if (is_callable($callback)) { - $callback($document); - } - - return true; - } else { - Console::error('Failed to delete document: ' . $document->getId()); - return false; - } - } - - /** - * @param callable $callback - * @throws Exception - */ - protected function deleteForProjectIds(callable $callback): void - { - // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document - $count = 0; - $chunk = 0; - $limit = 50; - $projects = []; - $sum = $limit; - - $executionStart = \microtime(true); - - while ($sum === $limit) { - $projects = $this->getConsoleDB()->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]); - - $chunk++; - - /** @var string[] $projectIds */ - $sum = count($projects); - - Console::info('Executing delete function for chunk #' . $chunk . '. Found ' . $sum . ' projects'); - foreach ($projects as $project) { - $callback($project); - $count++; - } - } - - $executionEnd = \microtime(true); - Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); - } - - /** - * @param string $collection collectionID - * @param array $queries - * @param Database $database - * @param callable|null $callback - * @throws Exception - */ - protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void - { - $count = 0; - $chunk = 0; - $limit = 50; - $results = []; - $sum = $limit; - - $executionStart = \microtime(true); - - try { - while ($sum === $limit) { - $chunk++; - - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); - - $sum = count($results); - - Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents in collection ' . $database->getNamespace() . '_' . $collection); - - foreach ($results as $document) { - $this->deleteById($document, $database, $callback); - $count++; - } - } - } catch (\Exception $e) { - Console::error($e->getMessage()); - } - - $executionEnd = \microtime(true); - - Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); - } - - /** - * @param string $collection collectionID - * @param Query[] $queries - * @param Database $database - * @param callable $callback - */ - protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void - { - $count = 0; - $chunk = 0; - $limit = 50; - $results = []; - $sum = $limit; - $cursor = null; - - $executionStart = \microtime(true); - - while ($sum === $limit) { - $chunk++; - - $mergedQueries = \array_merge([Query::limit($limit)], $queries); - if ($cursor instanceof Document) { - $mergedQueries[] = Query::cursorAfter($cursor); - } - - $results = $database->find($collection, $mergedQueries); - - $sum = count($results); - - if ($sum > 0) { - $cursor = $results[$sum - 1]; - } - - foreach ($results as $document) { - if (is_callable($callback)) { - $callback($document); - } - - $count++; - } - } - - $executionEnd = \microtime(true); - - Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); - } - - /** - * @param Document $document rule document - * @param Document $project project document - */ - protected function deleteRule(Document $document, Document $project): void - { - $consoleDB = $this->getConsoleDB(); - - $domain = $document->getAttribute('domain'); - $directory = APP_STORAGE_CERTIFICATES . '/' . $domain; - $checkTraversal = realpath($directory) === $directory; - - if ($checkTraversal && is_dir($directory)) { - // Delete files, so Traefik is aware of change - array_map('unlink', glob($directory . '/*.*')); - rmdir($directory); - Console::info("Deleted certificate files for {$domain}"); - } else { - Console::info("No certificate files found for {$domain}"); - } - - // Delete certificate document, so Appwrite is aware of change - if (isset($document['certificateId'])) { - $consoleDB->deleteDocument('certificates', $document['certificateId']); - } - } - - protected function deleteBucket(Document $document, Document $project) - { - $projectId = $project->getId(); - $dbForProject = $this->getProjectDB($project); - $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); - - $device = $this->getFilesDevice($projectId); - - $device->deletePath($document->getId()); - } - - protected function deleteInstallation(Document $document, Document $project) - { - $dbForProject = $this->getProjectDB($project); - $dbForConsole = $this->getConsoleDB(); - - $this->listByGroup('functions', [ - Query::equal('installationInternalId', [$document->getInternalId()]) - ], $dbForProject, function ($function) use ($dbForProject, $dbForConsole) { - $dbForConsole->deleteDocument('repositories', $function->getAttribute('repositoryId')); - - $function = $function - ->setAttribute('installationId', '') - ->setAttribute('installationInternalId', '') - ->setAttribute('providerRepositoryId', '') - ->setAttribute('providerBranch', '') - ->setAttribute('providerSilentMode', false) - ->setAttribute('providerRootDirectory', '') - ->setAttribute('repositoryId', '') - ->setAttribute('repositoryInternalId', ''); - $dbForProject->updateDocument('functions', $function->getId(), $function); - }); - } - - protected function deleteRuntimes(?Document $function, Document $project) - { - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); - - $deleteByFunction = function (Document $function) use ($project, $executor) { - $this->listByGroup( - 'deployments', - [ - Query::equal('resourceInternalId', [$function->getInternalId()]), - Query::equal('resourceType', ['functions']), - ], - $this->getProjectDB($project), - function (Document $deployment) use ($project, $executor) { - $deploymentId = $deployment->getId(); - - try { - $executor->deleteRuntime($project->getId(), $deploymentId); - Console::info("Runtime for deployment {$deploymentId} deleted."); - } catch (Throwable $th) { - Console::warning("Runtime for deployment {$deploymentId} skipped:"); - Console::error('[Error] Type: ' . get_class($th)); - Console::error('[Error] Message: ' . $th->getMessage()); - Console::error('[Error] File: ' . $th->getFile()); - Console::error('[Error] Line: ' . $th->getLine()); - } - } - ); - }; - - if ($function !== null) { - // Delete function runtimes - $deleteByFunction($function); - } else { - // Delete all project runtimes - $this->listByGroup( - 'functions', - [], - $this->getProjectDB($project), - function (Document $function) use ($deleteByFunction) { - $deleteByFunction($function); - } - ); - } - } -} diff --git a/app/workers/functions.php b/app/workers/functions.php deleted file mode 100644 index 619d33387a..0000000000 --- a/app/workers/functions.php +++ /dev/null @@ -1,426 +0,0 @@ -getId(); - $deploymentId = $function->getAttribute('deployment', ''); - - $log->addTag('functionId', $functionId); - $log->addTag('projectId', $project->getId()); - - /** Check if deployment exists */ - $deployment = $dbForProject->getDocument('deployments', $deploymentId); - - if ($deployment->getAttribute('resourceId') !== $functionId) { - throw new Exception('Deployment not found. Create deployment before trying to execute a function'); - } - - if ($deployment->isEmpty()) { - throw new Exception('Deployment not found. Create deployment before trying to execute a function'); - } - - /** Check if build has exists */ - $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); - if ($build->isEmpty()) { - throw new Exception('Build not found'); - } - - if ($build->getAttribute('status') !== 'ready') { - throw new Exception('Build not ready'); - } - - /** Check if runtime is supported */ - $version = $function->getAttribute('version', 'v2'); - $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); - - if (!\array_key_exists($function->getAttribute('runtime'), $runtimes)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); - } - - $runtime = $runtimes[$function->getAttribute('runtime')]; - - $headers['x-appwrite-trigger'] = $trigger; - $headers['x-appwrite-event'] = $event ?? ''; - $headers['x-appwrite-user-id'] = $user->getId() ?? ''; - $headers['x-appwrite-user-jwt'] = $jwt ?? ''; - - /** Create execution or update execution status */ - $execution = $dbForProject->getDocument('executions', $executionId ?? ''); - if ($execution->isEmpty()) { - $headersFiltered = []; - foreach ($headers as $key => $value) { - if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) { - $headersFiltered[] = [ 'name' => $key, 'value' => $value ]; - } - } - - $executionId = ID::unique(); - $execution = new Document([ - '$id' => $executionId, - '$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))], - 'functionInternalId' => $function->getInternalId(), - 'functionId' => $function->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), - 'deploymentId' => $deployment->getId(), - 'trigger' => $trigger, - 'status' => 'processing', - 'responseStatusCode' => 0, - 'responseHeaders' => [], - 'requestPath' => $path, - 'requestMethod' => $method, - 'requestHeaders' => $headersFiltered, - 'errors' => '', - 'logs' => '', - 'duration' => 0.0, - 'search' => implode(' ', [$functionId, $executionId]), - ]); - - if ($function->getAttribute('logging')) { - $execution = $dbForProject->createDocument('executions', $execution); - } - - // TODO: @Meldiron Trigger executions.create event here - - if ($execution->isEmpty()) { - throw new Exception('Failed to create or read execution'); - } - } - - if ($execution->getAttribute('status') !== 'processing') { - $execution->setAttribute('status', 'processing'); - - if ($function->getAttribute('logging')) { - $execution = $dbForProject->updateDocument('executions', $executionId, $execution); - } - } - - $durationStart = \microtime(true); - - $body = $eventData ?? ''; - if (empty($body)) { - $body = $data ?? ''; - } - - $vars = []; - - // V2 vars - if ($version === 'v2') { - $vars = \array_merge($vars, [ - 'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '', - 'APPWRITE_FUNCTION_DATA' => $body ?? '', - 'APPWRITE_FUNCTION_EVENT_DATA' => $body ?? '', - 'APPWRITE_FUNCTION_EVENT' => $headers['x-appwrite-event'] ?? '', - 'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '', - 'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? '' - ]); - } - - // Shared vars - foreach ($function->getAttribute('varsProject', []) as $var) { - $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); - } - - // Function vars - foreach ($function->getAttribute('vars', []) as $var) { - $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); - } - - // Appwrite vars - $vars = \array_merge($vars, [ - 'APPWRITE_FUNCTION_ID' => $functionId, - 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), - 'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId, - 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), - 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', - 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - ]); - - /** Execute function */ - try { - $version = $function->getAttribute('version', 'v2'); - $command = $runtime['startCommand']; - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); - $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; - $executionResponse = $executor->createExecution( - projectId: $project->getId(), - deploymentId: $deploymentId, - body: \strlen($body) > 0 ? $body : null, - variables: $vars, - timeout: $function->getAttribute('timeout', 0), - image: $runtime['image'], - source: $build->getAttribute('path', ''), - entrypoint: $deployment->getAttribute('entrypoint', ''), - version: $version, - path: $path, - method: $method, - headers: $headers, - runtimeEntrypoint: $command - ); - - $status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed'; - - $headersFiltered = []; - foreach ($executionResponse['headers'] as $key => $value) { - if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { - $headersFiltered[] = [ 'name' => $key, 'value' => $value ]; - } - } - - /** Update execution status */ - $execution - ->setAttribute('status', $status) - ->setAttribute('responseStatusCode', $executionResponse['statusCode']) - ->setAttribute('responseHeaders', $headersFiltered) - ->setAttribute('logs', $executionResponse['logs']) - ->setAttribute('errors', $executionResponse['errors']) - ->setAttribute('duration', $executionResponse['duration']); - } catch (\Throwable $th) { - $durationEnd = \microtime(true); - - $execution - ->setAttribute('duration', $durationEnd - $durationStart) - ->setAttribute('status', 'failed') - ->setAttribute('responseStatusCode', 500) - ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); - - $error = $th->getMessage(); - $errorCode = $th->getCode(); - } - - if ($function->getAttribute('logging')) { - $execution = $dbForProject->updateDocument('executions', $executionId, $execution); - } - - /** Trigger Webhook */ - $executionModel = new Execution(); - $executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); - $executionUpdate - ->setProject($project) - ->setUser($user) - ->setEvent('functions.[functionId].executions.[executionId].update') - ->setParam('functionId', $function->getId()) - ->setParam('executionId', $execution->getId()) - ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))) - ->trigger(); - - /** Trigger Functions */ - $queueForFunctions - ->from($executionUpdate) - ->trigger(); - - /** Trigger realtime event */ - $allEvents = Event::generateEvents('functions.[functionId].executions.[executionId].update', [ - 'functionId' => $function->getId(), - 'executionId' => $execution->getId() - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $execution - ); - Realtime::send( - projectId: 'console', - payload: $execution->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $execution->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - - /** Update usage stats */ - if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { - $usage = new Stats($statsd); - $usage - ->setParam('projectId', $project->getId()) - ->setParam('projectInternalId', $project->getInternalId()) - ->setParam('functionId', $function->getId()) // TODO: We should use functionInternalId in usage stats - ->setParam('executions.{scope}.compute', 1) - ->setParam('executionStatus', $execution->getAttribute('status', '')) - ->setParam('executionTime', $execution->getAttribute('duration')) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0) - ->submit(); - } - - if (!empty($error)) { - throw new Exception($error, $errorCode); - } - }; -}); - -$server->job() - ->inject('message') - ->inject('dbForProject') - ->inject('queueForFunctions') - ->inject('statsd') - ->inject('execute') - ->inject('log') - ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Client $statsd, callable $execute, Log $log) { - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - $type = $payload['type'] ?? ''; - $events = $payload['events'] ?? []; - $data = $payload['body'] ?? ''; - $eventData = $payload['payload'] ?? ''; - $project = new Document($payload['project'] ?? []); - $function = new Document($payload['function'] ?? []); - $user = new Document($payload['user'] ?? []); - - if ($project->getId() === 'console') { - return; - } - - if (!empty($events)) { - $limit = 30; - $sum = 30; - $offset = 0; - $functions = []; - /** @var Document[] $functions */ - while ($sum >= $limit) { - $functions = $dbForProject->find('functions', [ - Query::limit($limit), - Query::offset($offset) - ]); - - $sum = \count($functions); - $offset = $offset + $limit; - - Console::log('Fetched ' . $sum . ' functions...'); - - foreach ($functions as $function) { - if (!array_intersect($events, $function->getAttribute('events', []))) { - continue; - } - Console::success('Iterating function: ' . $function->getAttribute('name')); - $execute( - log: $log, - statsd: $statsd, - dbForProject: $dbForProject, - project: $project, - function: $function, - queueForFunctions: $queueForFunctions, - trigger: 'event', - event: $events[0], - eventData: \is_string($eventData) ? $eventData : \json_encode($eventData), - user: $user, - data: null, - executionId: null, - jwt: null, - path: '/', - method: 'POST', - headers: [ - 'user-agent' => 'Appwrite/' . APP_VERSION_STABLE - ], - ); - Console::success('Triggered function: ' . $events[0]); - } - } - return; - } - - /** - * Handle Schedule and HTTP execution. - */ - switch ($type) { - case 'http': - $jwt = $payload['jwt'] ?? ''; - $execution = new Document($payload['execution'] ?? []); - $user = new Document($payload['user'] ?? []); - $execute( - log: $log, - project: $project, - function: $function, - dbForProject: $dbForProject, - queueForFunctions: $queueForFunctions, - trigger: 'http', - executionId: $execution->getId(), - event: null, - eventData: null, - data: $data, - user: $user, - jwt: $jwt, - path: $payload['path'] ?? '', - method: $payload['method'] ?? 'POST', - headers: $payload['headers'] ?? [], - statsd: $statsd, - ); - break; - case 'schedule': - $execute( - log: $log, - project: $project, - function: $function, - dbForProject: $dbForProject, - queueForFunctions: $queueForFunctions, - trigger: 'schedule', - executionId: null, - event: null, - eventData: null, - data: null, - user: null, - jwt: null, - path: $payload['path'] ?? '/', - method: $payload['method'] ?? 'POST', - headers: $payload['headers'] ?? [], - statsd: $statsd, - ); - break; - } - }); - -$server->workerStart(); -$server->start(); diff --git a/app/workers/mails.php b/app/workers/mails.php deleted file mode 100644 index 56676b4aed..0000000000 --- a/app/workers/mails.php +++ /dev/null @@ -1,106 +0,0 @@ -args['smtp']; - - if (empty($smtp) && empty(App::getEnv('_APP_SMTP_HOST'))) { - Console::info('Skipped mail processing. No SMTP configuration has been set.'); - return; - } - - $recipient = $this->args['recipient']; - $subject = $this->args['subject']; - $body = $this->args['body']; - $variables = $this->args['variables']; - $name = $this->args['name']; - - $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); - - foreach ($variables as $key => $value) { - $body->setParam('{{' . $key . '}}', $value); - } - - $body = $body->render(); - - /** @var PHPMailer $mail */ - $mail = empty($smtp) - ? $register->get('smtp') - : $this->getMailer($smtp); - - $mail->clearAddresses(); - $mail->clearAllRecipients(); - $mail->clearReplyTos(); - $mail->clearAttachments(); - $mail->clearBCCs(); - $mail->clearCCs(); - $mail->addAddress($recipient, $name); - $mail->Subject = $subject; - $mail->Body = $body; - $mail->AltBody = \strip_tags($body); - - try { - $mail->send(); - } catch (\Exception $error) { - throw new Exception('Error sending mail: ' . $error->getMessage(), 500); - } - } - - protected function getMailer(array $smtp): PHPMailer - { - $mail = new PHPMailer(true); - - $mail->isSMTP(); - - $username = $smtp['username']; - $password = $smtp['password']; - - $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = $smtp['host']; - $mail->Port = $smtp['port']; - $mail->SMTPAuth = (!empty($username) && !empty($password)); - $mail->Username = $username; - $mail->Password = $password; - $mail->SMTPSecure = $smtp['secure']; - $mail->SMTPAutoTLS = false; - $mail->CharSet = 'UTF-8'; - - $mail->setFrom($smtp['senderEmail'], $smtp['senderName']); - - if (!empty($smtp['replyTo'])) { - $mail->addReplyTo($smtp['replyTo'], $smtp['senderName']); - } - - $mail->isHTML(); - - return $mail; - } - - public function shutdown(): void - { - } -} diff --git a/app/workers/messaging.php b/app/workers/messaging.php deleted file mode 100644 index 1c2b96c10b..0000000000 --- a/app/workers/messaging.php +++ /dev/null @@ -1,90 +0,0 @@ -getUser(); -$secret = $dsn->getPassword(); - -Server::setResource('sms', function () use ($dsn, $user, $secret) { - return match ($dsn->getHost()) { - 'mock' => new Mock($user, $secret), // used for tests - 'twilio' => new Twilio($user, $secret), - 'text-magic' => new TextMagic($user, $secret), - 'telesign' => new Telesign($user, $secret), - 'msg91' => new Msg91($user, $secret), - 'vonage' => new Vonage($user, $secret), - default => null - }; -}); - -Server::setResource('execute', function () { - return function (string $recipient, string $message, Adapter $sms) { - $from = App::getEnv('_APP_SMS_FROM'); - - if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { - Console::info('Skipped sms processing. No Phone provider has been set.'); - return; - } - - if (empty($from)) { - Console::info('Skipped sms processing. No phone number has been set.'); - return; - } - - $message = new SMS( - to: [$recipient], - content: $message, - from: $from, - ); - - try { - $sms->send($message); - } catch (\Exception $error) { - throw new Exception('Error sending message: ' . $error->getMessage(), 500); - } - }; -}); - -$server->job() - ->inject('message') - ->inject('execute') - ->inject('sms') - ->action(function (Message $message, callable $execute, Adapter $sms) { - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - if (empty($payload['recipient'])) { - throw new Exception('Missing recipient'); - } - - if (empty($payload['message'])) { - throw new Exception('Missing message'); - } - - $execute($payload['recipient'], $payload['message'], $sms); - }); - -$server->workerStart(); -$server->start(); diff --git a/app/workers/migrations.php b/app/workers/migrations.php deleted file mode 100644 index 0f8194acf8..0000000000 --- a/app/workers/migrations.php +++ /dev/null @@ -1,308 +0,0 @@ -args['type'] ?? ''; - $events = $this->args['events'] ?? []; - $project = new Document($this->args['project'] ?? []); - $user = new Document($this->args['user'] ?? []); - $payload = json_encode($this->args['payload'] ?? []); - - if ($project->getId() === 'console') { - return; - } - - /** - * Handle Event execution. - */ - if (! empty($events)) { - return; - } - - $this->dbForProject = $this->getProjectDB($project); - - $this->processMigration(); - } - - /** - * Process Source - * - * @return Source - * - * @throws \Exception - */ - protected function processSource(string $source, array $credentials): Source - { - switch ($source) { - case Firebase::getName(): - return new Firebase( - json_decode($credentials['serviceAccount'], true), - ); - break; - case Supabase::getName(): - return new Supabase( - $credentials['endpoint'], - $credentials['apiKey'], - $credentials['databaseHost'], - 'postgres', - $credentials['username'], - $credentials['password'], - $credentials['port'], - ); - break; - case NHost::getName(): - return new NHost( - $credentials['subdomain'], - $credentials['region'], - $credentials['adminSecret'], - $credentials['database'], - $credentials['username'], - $credentials['password'], - $credentials['port'], - ); - break; - case Appwrite::getName(): - return new Appwrite($credentials['projectId'], str_starts_with($credentials['endpoint'], 'http://localhost/v1') ? 'http://appwrite/v1' : $credentials['endpoint'], $credentials['apiKey']); - break; - default: - throw new \Exception('Invalid source type'); - break; - } - } - - protected function updateMigrationDocument(Document $migration, Document $project): Document - { - /** Trigger Realtime */ - $allEvents = Event::generateEvents('migrations.[migrationId].update', [ - 'migrationId' => $migration->getId(), - ]); - - $target = Realtime::fromPayload( - event: $allEvents[0], - payload: $migration, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $migration->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - ); - - Realtime::send( - projectId: $project->getId(), - payload: $migration->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - ); - - return $this->dbForProject->updateDocument('migrations', $migration->getId(), $migration); - } - - protected function removeAPIKey(Document $apiKey) - { - $consoleDB = $this->getConsoleDB(); - - $consoleDB->deleteDocument('keys', $apiKey->getId()); - } - - protected function generateAPIKey(Document $project): Document - { - $consoleDB = $this->getConsoleDB(); - $generatedSecret = bin2hex(\random_bytes(128)); - - $key = new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'projectInternalId' => $project->getInternalId(), - 'projectId' => $project->getId(), - 'name' => 'Transfer API Key', - 'scopes' => [ - 'users.read', - 'users.write', - 'teams.read', - 'teams.write', - 'databases.read', - 'databases.write', - 'collections.read', - 'collections.write', - 'documents.read', - 'documents.write', - 'buckets.read', - 'buckets.write', - 'files.read', - 'files.write', - 'functions.read', - 'functions.write', - ], - 'expire' => null, - 'sdks' => [], - 'accessedAt' => null, - 'secret' => $generatedSecret, - ]); - - $consoleDB->createDocument('keys', $key); - $consoleDB->deleteCachedDocument('projects', $project->getId()); - - return $key; - } - - /** - * Process Migration - * - * @return void - */ - protected function processMigration(): void - { - /** - * @var Document $migrationDocument - * @var Transfer $transfer - */ - $migrationDocument = null; - $transfer = null; - $projectDocument = $this->getConsoleDB()->getDocument('projects', $this->args['project']['$id']); - $tempAPIKey = $this->generateAPIKey($projectDocument); - - try { - $migrationDocument = $this->dbForProject->getDocument('migrations', $this->args['migration']['$id']); - $migrationDocument->setAttribute('stage', 'processing'); - $migrationDocument->setAttribute('status', 'processing'); - $this->updateMigrationDocument($migrationDocument, $projectDocument); - - $source = $this->processSource($migrationDocument->getAttribute('source'), $migrationDocument->getAttribute('credentials')); - - $source->report(); - - $destination = new DestinationsAppwrite( - $projectDocument->getId(), - 'http://appwrite/v1', - $tempAPIKey['secret'], - ); - - $transfer = new Transfer( - $source, - $destination - ); - - /** Start Transfer */ - $migrationDocument->setAttribute('stage', 'migrating'); - $this->updateMigrationDocument($migrationDocument, $projectDocument); - $transfer->run($migrationDocument->getAttribute('resources'), function () use ($migrationDocument, $transfer, $projectDocument) { - $migrationDocument->setAttribute('resourceData', json_encode($transfer->getCache())); - $migrationDocument->setAttribute('statusCounters', json_encode($transfer->getStatusCounters())); - - $this->updateMigrationDocument($migrationDocument, $projectDocument); - }); - - $errors = $transfer->getReport(Resource::STATUS_ERROR); - - if (count($errors) > 0) { - $migrationDocument->setAttribute('status', 'failed'); - $migrationDocument->setAttribute('stage', 'finished'); - - $errorMessages = []; - foreach ($errors as $error) { - $errorMessages[] = "Failed to transfer resource '{$error['id']}:{$error['resource']}' with message '{$error['message']}'"; - } - - $migrationDocument->setAttribute('errors', $errorMessages); - $this->updateMigrationDocument($migrationDocument, $projectDocument); - - return; - } - - $migrationDocument->setAttribute('status', 'completed'); - $migrationDocument->setAttribute('stage', 'finished'); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - - if ($migrationDocument) { - Console::error($th->getMessage()); - Console::error($th->getTraceAsString()); - $migrationDocument->setAttribute('status', 'failed'); - $migrationDocument->setAttribute('stage', 'finished'); - $migrationDocument->setAttribute('errors', [$th->getMessage()]); - - return; - } - - if ($transfer) { - $errors = $transfer->getReport(Resource::STATUS_ERROR); - - if (count($errors) > 0) { - $migrationDocument->setAttribute('status', 'failed'); - $migrationDocument->setAttribute('stage', 'finished'); - $migrationDocument->setAttribute('errors', $errors); - } - } - } finally { - if ($migrationDocument) { - $this->updateMigrationDocument($migrationDocument, $projectDocument); - } - if ($tempAPIKey) { - $this->removeAPIKey($tempAPIKey); - } - } - } - - /** - * Process Verification - * - * @return void - */ - protected function processVerification(): void - { - } - - public function shutdown(): void - { - } -} diff --git a/app/workers/webhooks.php b/app/workers/webhooks.php deleted file mode 100644 index d98ff73a43..0000000000 --- a/app/workers/webhooks.php +++ /dev/null @@ -1,98 +0,0 @@ -getAttribute('url')); - $signatureKey = $webhook->getAttribute('signatureKey'); - $signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true)); - $httpUser = $webhook->getAttribute('httpUser'); - $httpPass = $webhook->getAttribute('httpPass'); - $ch = \curl_init($webhook->getAttribute('url')); - - \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - \curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - \curl_setopt($ch, CURLOPT_HEADER, 0); - \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - \curl_setopt($ch, CURLOPT_USERAGENT, \sprintf( - APP_USERAGENT, - App::getEnv('_APP_VERSION', 'UNKNOWN'), - App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) - )); - \curl_setopt( - $ch, - CURLOPT_HTTPHEADER, - [ - 'Content-Type: application/json', - 'Content-Length: ' . \strlen($payload), - 'X-' . APP_NAME . '-Webhook-Id: ' . $webhook->getId(), - 'X-' . APP_NAME . '-Webhook-Events: ' . implode(',', $events), - 'X-' . APP_NAME . '-Webhook-Name: ' . $webhook->getAttribute('name', ''), - 'X-' . APP_NAME . '-Webhook-User-Id: ' . $user->getId(), - 'X-' . APP_NAME . '-Webhook-Project-Id: ' . $project->getId(), - 'X-' . APP_NAME . '-Webhook-Signature: ' . $signature, - ] - ); - - if (!$webhook->getAttribute('security', true)) { - \curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - \curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - } - - if (!empty($httpUser) && !empty($httpPass)) { - \curl_setopt($ch, CURLOPT_USERPWD, "$httpUser:$httpPass"); - \curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - } - - if (false === \curl_exec($ch)) { - $errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name'); - } - - \curl_close($ch); - }; -}); - - -$server->job() - ->inject('message') - ->inject('execute') - ->action(function (Message $message, callable $execute) { - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - $events = $payload['events']; - $webhookPayload = json_encode($payload['payload']); - $project = new Document($payload['project']); - $user = new Document($payload['user'] ?? []); - - foreach ($project->getAttribute('webhooks', []) as $webhook) { - if (array_intersect($webhook->getAttribute('events', []), $events)) { - $execute($events, $webhookPayload, $webhook, $user, $project); - } - } - - if (!empty($errors)) { - throw new Exception(\implode(" / \n\n", $errors)); - } - }); - - -$server->workerStart(); -$server->start(); diff --git a/composer.json b/composer.json index b5d2392406..243906e99a 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "utopia-php/preloader": "0.2.*", "utopia-php/queue": "dev-feat-get-worker-start as 0.5.3", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.14.*", + "utopia-php/storage": "0.17.*", "utopia-php/swoole": "0.5.*", "utopia-php/vcs": "0.5.*", "utopia-php/websocket": "0.1.*", diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 745510c9e9..3eb411d482 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -147,6 +147,7 @@ class Builds extends Action $durationStart = \microtime(true); $buildId = $deployment->getAttribute('buildId', ''); $isNewBuild = empty($buildId); + $deviceFunctions = $getFunctionsDevice($project->getId()); if ($isNewBuild) { $buildId = ID::unique(); @@ -160,7 +161,7 @@ class Builds extends Action 'path' => '', 'runtime' => $function->getAttribute('runtime'), 'source' => $deployment->getAttribute('path', ''), - 'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)), + 'sourceType' => strtolower($deviceFunctions->getType()), 'logs' => '', 'endTime' => null, 'duration' => 0, @@ -287,17 +288,24 @@ class Builds extends Action $tmpPath = '/tmp/builds/' . \escapeshellcmd($buildId); $tmpPathFile = $tmpPath . '/code.tar.gz'; + $localDevice = new Local(); + + if (substr($tmpDirectory, -1) !== '/') { + $tmpDirectory .= '/'; + } + + $directorySize = $localDevice->getDirectorySize($tmpDirectory); + $functionsSizeLimit = (int) App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'); + if ($directorySize > $functionsSizeLimit) { + throw new Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); + } Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); $deviceFunctions = $getFunctionsDevice($project->getId()); - $localDevice = new Local(); - $buffer = $localDevice->read($tmpPathFile); - $mimeType = $localDevice->getFileMimeType($tmpPathFile); - $path = $deviceFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); - $result = $deviceFunctions->write($path, $buffer, $mimeType); + $result = $localDevice->transfer($tmpPathFile, $path, $deviceFunctions); if (!$result) { throw new \Exception("Unable to move file"); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index e76dd29032..896ad40bf9 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -88,16 +88,16 @@ class Deletes extends Action $this->deleteUser($getProjectDB, $document, $project); break; case DELETE_TYPE_TEAMS: - $this->deleteMemberships($document, $project); + $this->deleteMemberships($getProjectDB, $document, $project); if ($project->getId() === 'console') { - $this->deleteProjectsByTeam($dbForConsole, $document); + $this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document); } break; case DELETE_TYPE_BUCKETS: $this->deleteBucket($getProjectDB, $getFilesDevice, $document, $project); break; case DELETE_TYPE_INSTALLATIONS: - $this->deleteInstallation($dbForConsole, $document, $project); + $this->deleteInstallation($dbForConsole, $getProjectDB, $document, $project); break; case DELETE_TYPE_RULES: $this->deleteRule($dbForConsole, $document, $project); @@ -384,15 +384,14 @@ class Deletes extends Action * @throws Restricted * @throws Structure */ - protected function deleteProjectsByTeam(Database $dbForConsole, Document $document): void + protected function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void { $projects = $dbForConsole->find('projects', [ Query::equal('teamInternalId', [$document->getInternalId()]) ]); - foreach ($projects as $project) { - $this->deleteProject($dbForConsole, $project); + $this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $project); $dbForConsole->deleteDocument('projects', $project->getId()); } } @@ -778,8 +777,6 @@ class Deletes extends Action $this->deleteRuntimes($getProjectDB, $document, $project); } - - /** * @param Document $document to be deleted * @param Database $database to delete it from diff --git a/tests/e2e/Services/Avatars/AvatarsBase.php b/tests/e2e/Services/Avatars/AvatarsBase.php index 1652e01514..ba66920ed6 100644 --- a/tests/e2e/Services/Avatars/AvatarsBase.php +++ b/tests/e2e/Services/Avatars/AvatarsBase.php @@ -202,7 +202,7 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/image', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/images/apple.png', + 'url' => 'https://appwrite.io/images/open-graph/website.png', ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -212,7 +212,7 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/image', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/images/apple.png', + 'url' => 'https://appwrite.io/images/open-graph/website.png', 'width' => 200, 'height' => 200, ]); @@ -224,7 +224,7 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/image', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/images/apple.png', + 'url' => 'https://appwrite.io/images/open-graph/website.png', 'width' => 300, 'height' => 300, 'quality' => 30, @@ -251,7 +251,7 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/image', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/images/apple.png', + 'url' => 'https://appwrite.io/images/open-graph/website.png', 'width' => 2001, 'height' => 300, 'quality' => 30, @@ -280,11 +280,11 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/favicon', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/', + 'url' => 'https://github.com/', ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('image/png', $response['headers']['content-type']); + $this->assertEquals('image/x-icon', $response['headers']['content-type']); $this->assertNotEmpty($response['body']); /** @@ -524,4 +524,28 @@ trait AvatarsBase $this->assertEquals('PNG', $image->getImageFormat()); $this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/initials.png')), strlen($response['body'])); } + + public function testSpecialCharsInitalImage() + { + $response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [ + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'name' => 'W (Hello) W', + 'width' => 200, + 'height' => 200, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('image/png', $response['headers']['content-type']); + $this->assertNotEmpty($response['body']); + + $image = new \Imagick(); + $image->readImageBlob($response['body']); + $original = new \Imagick(__DIR__ . '/../../../resources/initials.png'); + + $this->assertEquals($image->getImageWidth(), $original->getImageWidth()); + $this->assertEquals($image->getImageHeight(), $original->getImageHeight()); + $this->assertEquals('PNG', $image->getImageFormat()); + $this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/initials.png')), strlen($response['body'])); + } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 60b7f7542e..cdc9ec846f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -32,8 +32,8 @@ class FunctionsCustomServerTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [ - 'users.*.create', - 'users.*.delete', + 'buckets.*.create', + 'buckets.*.delete', ], 'schedule' => '0 0 1 1 *', 'timeout' => 10, @@ -50,8 +50,8 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, $dateValidator->isValid($response1['body']['$updatedAt'])); $this->assertEquals('', $response1['body']['deployment']); $this->assertEquals([ - 'users.*.create', - 'users.*.delete', + 'buckets.*.create', + 'buckets.*.delete', ], $response1['body']['events']); $this->assertEquals('0 0 1 1 *', $response1['body']['schedule']); $this->assertEquals(10, $response1['body']['timeout']); @@ -191,8 +191,8 @@ class FunctionsCustomServerTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [ - 'users.*.create', - 'users.*.delete', + 'buckets.*.create', + 'buckets.*.delete', ], 'schedule' => '0 0 1 1 *', 'timeout' => 10, @@ -1231,4 +1231,117 @@ class FunctionsCustomServerTest extends Scope $this->assertArrayHasKey('base', $runtime); $this->assertArrayHasKey('supports', $runtime); } + + + public function testEventTrigger() + { + $timeout = 5; + $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-event/code.tar.gz"; + $this->packageCode('php-event'); + + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'functionId' => ID::unique(), + 'name' => 'Test PHP Event executions', + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'events' => [ + 'users.*.create', + ], + 'timeout' => $timeout, + ]); + + $functionId = $function['body']['$id'] ?? ''; + + $this->assertEquals(201, $function['headers']['status-code']); + + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'entrypoint' => 'index.php', + 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'activate' => true + ]); + + $deploymentId = $deployment['body']['$id'] ?? ''; + $this->assertEquals(202, $deployment['headers']['status-code']); + + // Poll until deployment is built + while (true) { + $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + if ( + $deployment['headers']['status-code'] >= 400 + || \in_array($deployment['body']['status'], ['ready', 'failed']) + ) { + break; + } + + \sleep(1); + } + + $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $deployment['headers']['status-code']); + + // Wait a little for activation to finish + sleep(5); + + // Create user to trigger event + $user = $this->client->call(Client::METHOD_POST, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'userId' => 'unique()', + 'name' => 'Event User' + ]); + + $userId = $user['body']['$id']; + + $this->assertEquals(201, $user['headers']['status-code']); + + // Wait for execution to occur + sleep(15); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $execution = $executions['body']['executions'][0]; + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $execution['status']); + $this->assertEquals(204, $execution['responseStatusCode']); + $this->assertStringContainsString($userId, $execution['logs']); + $this->assertStringContainsString('Event User', $execution['logs']); + + // Cleanup : Delete function + $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], []); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Cleanup : Delete user + $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], []); + + $this->assertEquals(204, $response['headers']['status-code']); + } } diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index cfb9061df3..b95e3b251f 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -155,7 +155,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(4959, \strlen($initials['body'])); + $this->assertEquals(5041, \strlen($initials['body'])); return $initials['body']; } diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 96c9bde5c7..4ac15bd95c 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -29,10 +29,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['ping']); $this->assertLessThan(100, $response['body']['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -51,10 +47,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -73,10 +65,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -95,10 +83,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -117,10 +101,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -141,10 +121,6 @@ class HealthCustomServerTest extends Scope $this->assertNotEmpty($response['body']['localTime']); $this->assertLessThan(10, $response['body']['diff']); - /** - * Test for FAILURE - */ - return []; } @@ -162,10 +138,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); - /** - * Test for FAILURE - */ - return []; } @@ -183,10 +155,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); - /** - * Test for FAILURE - */ - return []; } @@ -204,9 +172,124 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); + return []; + } + + public function testFunctionsSuccess(): array + { /** - * Test for FAILURE + * Test for SUCCESS */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testBuildsSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/builds', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testDatabasesSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testDeletesSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/deletes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testMailsSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/mails', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testMessagingSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/messaging', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testMigrationsSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/migrations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); return []; } @@ -226,10 +309,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['ping']); $this->assertLessThan(100, $response['body']['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -248,10 +327,6 @@ class HealthCustomServerTest extends Scope $this->assertIsString($response['body']['status']); $this->assertIsString($response['body']['version']); - /** - * Test for FAILURE - */ - return []; } } diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index baf601789a..3327bb7558 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -595,6 +595,18 @@ trait UsersBase $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => '>', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertEmpty($response['body']['users']); + $this->assertCount(0, $response['body']['users']); + /** * Test for FAILURE */ diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 3baae7316d..bdb9bf49a4 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -66,7 +66,9 @@ services: environment: - _APP_ENV - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_FUNCTIONS diff --git a/tests/resources/initials.png b/tests/resources/initials.png index 08a61594c612009d6c7cb0b805a27676e5762815..ea6bf729b50f2b71b746690522a2cb0f2d1f0d17 100644 GIT binary patch literal 3075 zcmeHIX*kqt8~>XTGPW_vHjZVi>Bw4?J!ZzfO)6U=A(Q14Luiz-W@s$gqC`$k#26xm zLCKKlWX)J&#-S9M!C0nxo%i$m?R`JIAI^2%&;7gZ<$A8?{yq0~{W4sgQKG_f!T z+M#XS_mTeh5d!b~l2pIiec%taajJ=Me*idH1OU>J_uJe|0RU9%>WHyTVNCvG|1j|X&%n>0Ki^4_a`qQ`;kvuC2T+9n zO6i_^67+|m8!`21$SO$UyTjlbSpC@>p4g!u#;hypedlgI(y&b_f8~F0`}CgpFBLRx z*J;1_sKOHY#Fp8~^;3dd2zcmQ#zjMP^ zDSS!Nu4?Ak84=Ol&(Wp5F8XKN7m}qeYg+xOiWcIkF|(UGvG$_^3HL6Xe9TD;xW#y` z7-n9)a(#HyEAFAE<;Y(o$d2Q+BU!b%FMfY{De1xcc{ACr6XWm?x>UU{xPgw~W|h0n zGM|R(YLHsO$9p^o${!0XV3C#6X7*|O)A9v4plq0AfoGWjAjq(@vBJboef8dZ;B@8? zEspTSH1~B#NWQ5Gi&RC`P(74UEo1~!zKk)*EfMI@Re!BLimuV@P*v%0&@|KP*t+n( zb$kl9mbf*04d1%gR5HB#+)V1>e14nLbj zU<)@BkU7z(S}!Ym2h`HaUm}MfUW4`_0<9g{B_wb1-f`5;rx~x&c|M4l?5uvh{xIZf z5oqQaxB2-&n1_O60{6|E3_HJi)GFaeMmD$EXL*$M~w| zU-R<`+SepE%wz?xGJid3{n4^amUy?WVevMbmbeh#=>-uG&u%L79`J?c{ zp7j8EGIYRHu9SN5(n0rMC@k?_#!^=ak`I3DEm>DF&6#6s`jBd(u)!ZWaCE2go1wsj zpNm+(rvX~L3!oQJTwqMklDwDfJc^h1R7 zl=s7qG1c(oY@T!_@^(Kei*@wCtOF8-H%+-B^zmzBKzp~3h=M_-c~3v8P|8ohuZC$& zmwYIUZ)J+AcebrI$Q)ExG-3;X^D>-Z)>~6ukulbTl*4IUQ9m!7^rLUj|-}kUj zk)2Z;#PoGm+$c{2RTns)a>iUj8D zRzvp4c(}S{L(2?GGmi0x&JdrWXF$BNXI1Ae-TrEZq>lhu9F7F_GqOH%qpKttf@Dcz zM)Y*ry|5LZ4G~0yL8BrqRnVQBqlBr^!&zdAJ9;+Z922P5Y}>_;{w#aCU0|XoQ^H2P z+S(YmNsd%Wr;Z8fv{aQhmMXhHg9Rzz&ZT~fOGsv*CE}h!slEv+h*-{rP~G)Us=q%z zE0RX{YauDIJrV4fbuFZMvL!eJr@HxtOK)v6J|WX!xMt z!q-090Zjt+C0&nhT#lUwxmToQoY#yZVVa<;>& zdspco(hZ`2Y)zkyVDVJl7jNDw*#CuMUtx=vQGHWcm;7dfs>~A<@HI8pvrl&>GBi3( z-X0DRgAWLjVa0{G)xGahjn0|a$&fP`tlC1U$K0stM$J^h{u|}agU|1=R;?yhVB=BO zSSRKvIa)>35xO0H>d4LHN_#=rN^A52&iJHnJ1OhUDdGmnPUs+RpE^dQoET3& z0Nmfsea(!BJU*(wsG~9-B^ad^`WOu^zK4_FK~w}f)GZMr!=)iwYsVY4zK$3SX|>5n zrBRn6q*2X0VeY5R3DmBEf}(68SH|SgVqx4+jZa{V-7q?J(G1zx#PbMtN6;(Uq8m3qSAhfFckox}Clv~NmbO-wyYsjQ@?yFW%`1CO z8XSlxV)J%R5fn$%u2InPvISf@lV3UFWBTtc(C|**Dq_~aL^nS5$8h)mS^@uqn7RgM a@gR$l-xpd7N`v+_AF#7^ws~glfBRqQvtCjF literal 3112 zcmeHI`!~~%8-FvlvCV|iwbDrls=R8?>4=04Qk~9DS zh{MkIUR#m(w?RQ$zAXJDcPmJQ;oNZm(2xh)43XT*)d|jC?f{Um8vs($0APKKN|^xw ziXi~Z1p@%K1OODzvf4Zk0f6``yqk~1FZRH{_;&;UKO6YozIo{hAcR3Ln?)k} z+&mxz9p`QPymDL7L#I23n5Vt_dDXEAXR%LOWzubKUwk{#E}!~S-YlW7BIQ}pzMY#H zW9P)bemJbL-Bz8G70h3Tgi%^Gt>GJ!aSNI6ozx6WN>0xCTzlldUK(QPph94vNAq!A ztiKAsODW&oB|{6S-aoH8(+quUfuS~D2uf;y=otM**Xmu&Z+_Q@itjvm9XPP#Wu&Kce$h#AtSZ*8 zlEgIoY-wZ(^IbjLiwG^v+vLJO8MZi-pT{qkN}Xm{eT&^Xv*$P7pqgn?s4i2iCKh3geL0W`ugDG*b>f{i zIu=XWHsH#)8pB`knneFSkK@r8_z2d@XflZioeOk&maA^7IU?VaUqhwyzmJ%ybAAucWJ^w_MBuFDX4v@8|1Q-C{-G~z3`ihMYf zr=fRkqW2rw7FWui^6fhg28T1&->&Ooi~K#?0=Xu!IZfoavIxeV?rca<-D= zL-^@Jpk^BgGuTbMBbEZpX@a2aILRo_$2&*L}OGYU{vB!^9=2*$( zG&e|dTP)e;BkZF|Dv}@d?LZgh;O6r&1kgdHuD$NQY?0#^dl=)YlDB#X<{V7Ad_68EzW-%?UX&Qjs?h(AZ_&|i*ZB~0$Bk20VJ{XBN^MC(E`s-I z;ag3w{HYUh_R1X<^9%P*l7%QN6#F`9M^SHW1b@gLF);Y;JWXUCPYWE@t9=MpA)3-0 zDqnCu!fH*xEKui~rcWSjp&XMPnK^TAP}P@ zZnYY>mtGg*Q6>j&-N!h+U+}gVg|fa5JG!^EL1sF zbQiC8saa?@3`uhE>j;Nog{I{sCve=bC?gWG#RLZaSt*J_H+q`N`bv+k*QMRhy2L#)OrAVeT@+RB+-S`=ijs}{urgNgyfC8QisF!A+3#xd z(EB*5x_}Qoee$Q#A z@5pl#>4I-e+&Js4*F|b4r%q~Zui zaz!1cRFs6XN!sQn?9gtq8|`vEhyHr*0A+3H0$8k|h%^4M0{QTf506~i&XMhzs*;eh zrnjY{-fV_;R;wiIk7B{;ae=l9%DI`Jz)Y2k7&8byjDNDLIPO;sq zqt8-m{`xKR$1iT!H0F-cdp!m^!^NOUSrHk%_2hxW4j%S(w!yUj09WC4 A&j0`b diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index a21ee92a5f..a328c8d599 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -3,12 +3,9 @@ namespace Tests\Unit\Event; use Appwrite\Event\Event; -use Appwrite\URL\URL; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Utopia\App; -use Utopia\DSN\DSN; -use Utopia\Queue; class EventTest extends TestCase { @@ -17,24 +14,12 @@ class EventTest extends TestCase public function setUp(): void { - $fallbackForRedis = URL::unparse([ - 'scheme' => 'redis', - 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => App::getEnv('_APP_REDIS_USER', ''), - 'pass' => App::getEnv('_APP_REDIS_PASS', ''), - ]); - - $dsn = App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis); - $dsn = explode('=', $dsn); - $dsn = $dsn[1] ?? ''; - - $dsn = new DSN($dsn); - - $connection = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); + $redisHost = App::getEnv('_APP_REDIS_HOST', ''); + $redisPort = App::getEnv('_APP_REDIS_PORT', ''); + \Resque::setBackend($redisHost . ':' . $redisPort); $this->queue = 'v1-tests' . uniqid(); - $this->object = new Event($this->queue, 'TestsV1', $connection); + $this->object = new Event($this->queue, 'TestsV1'); } public function testQueue(): void @@ -70,7 +55,7 @@ class EventTest extends TestCase $this->assertEquals('eventValue1', $this->object->getParam('eventKey1')); $this->assertEquals('eventValue2', $this->object->getParam('eventKey2')); $this->assertEquals(null, $this->object->getParam('eventKey3')); - $this->assertEquals($this->object->getQueueSize(), 1); + $this->assertEquals(\Resque::size($this->queue), 1); } public function testPause(): void From 3b00e396d5e662d4318b294175b6450d9e95c485 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 3 Oct 2023 15:15:13 +0300 Subject: [PATCH 053/144] sync with main --- docs/references/account/create-email-verification.md | 2 +- docs/references/account/create-magic-url-session.md | 2 +- docs/references/account/create-phone-session.md | 2 +- docs/references/account/create-phone-verification.md | 2 +- docs/references/account/create-recovery.md | 2 +- docs/references/account/create-session-anonymous.md | 2 +- docs/references/account/create.md | 2 +- docs/references/account/delete-session.md | 2 +- docs/references/account/update-magic-url-session.md | 2 +- docs/references/account/update-phone-session.md | 2 +- docs/references/account/update-phone.md | 2 +- docs/references/avatars/get-browser.md | 2 +- docs/references/teams/create-team-membership.md | 2 +- docs/references/teams/get-team-prefs.md | 2 +- docs/sdks/dart/CHANGELOG.md | 2 +- docs/sdks/flutter/CHANGELOG.md | 6 +++--- docs/services/account.md | 4 ++-- docs/tutorials/add-oauth2-provider.md | 2 +- src/Appwrite/Migration/Migration.php | 1 + src/Appwrite/Platform/Tasks/Doctor.php | 6 ++++++ src/Appwrite/Utopia/Database/Validator/Queries/Base.php | 1 + src/Executor/Executor.php | 6 ++++-- 22 files changed, 33 insertions(+), 23 deletions(-) diff --git a/docs/references/account/create-email-verification.md b/docs/references/account/create-email-verification.md index d4fc2dabb4..2e09f50e79 100644 --- a/docs/references/account/create-email-verification.md +++ b/docs/references/account/create-email-verification.md @@ -1,3 +1,3 @@ -Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](/docs/client/account#accountUpdateEmailVerification). The verification link sent to the user's email address is valid for 7 days. +Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](/docs/references/cloud/client-web/account#updateVerification). The verification link sent to the user's email address is valid for 7 days. Please note that in order to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface. diff --git a/docs/references/account/create-magic-url-session.md b/docs/references/account/create-magic-url-session.md index b1e63e144c..c34dcb7201 100644 --- a/docs/references/account/create-magic-url-session.md +++ b/docs/references/account/create-magic-url-session.md @@ -1,3 +1,3 @@ -Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [PUT /account/sessions/magic-url](/docs/client/account#accountUpdateMagicURLSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default. +Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [PUT /account/sessions/magic-url](updateMagicURLSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default. A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication-security#limits). diff --git a/docs/references/account/create-phone-session.md b/docs/references/account/create-phone-session.md index 8c8a1aebe5..15547668bc 100644 --- a/docs/references/account/create-phone-session.md +++ b/docs/references/account/create-phone-session.md @@ -1,3 +1,3 @@ -Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes. +Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/references/cloud/client-web/account#updatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes. A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication-security#limits). \ No newline at end of file diff --git a/docs/references/account/create-phone-verification.md b/docs/references/account/create-phone-verification.md index 28627b439f..467e6210dc 100644 --- a/docs/references/account/create-phone-verification.md +++ b/docs/references/account/create-phone-verification.md @@ -1 +1 @@ -Use this endpoint to send a verification SMS to the currently logged in user. This endpoint is meant for use after updating a user's phone number using the [accountUpdatePhone](/docs/client/account#accountUpdatePhone) endpoint. Learn more about how to [complete the verification process](/docs/client/account#accountUpdatePhoneVerification). The verification code sent to the user's phone number is valid for 15 minutes. \ No newline at end of file +Use this endpoint to send a verification SMS to the currently logged in user. This endpoint is meant for use after updating a user's phone number using the [accountUpdatePhone](/docs/references/cloud/client-web/account#updatePhone) endpoint. Learn more about how to [complete the verification process](/docs/references/cloud/client-web/account#updatePhoneVerification). The verification code sent to the user's phone number is valid for 15 minutes. \ No newline at end of file diff --git a/docs/references/account/create-recovery.md b/docs/references/account/create-recovery.md index 0b10fba210..4cc4699529 100644 --- a/docs/references/account/create-recovery.md +++ b/docs/references/account/create-recovery.md @@ -1 +1 @@ -Sends the user an email with a temporary secret key for password reset. When the user clicks the confirmation link he is redirected back to your app password reset URL with the secret key and email address values attached to the URL query string. Use the query string params to submit a request to the [PUT /account/recovery](/docs/client/account#accountUpdateRecovery) endpoint to complete the process. The verification link sent to the user's email address is valid for 1 hour. \ No newline at end of file +Sends the user an email with a temporary secret key for password reset. When the user clicks the confirmation link he is redirected back to your app password reset URL with the secret key and email address values attached to the URL query string. Use the query string params to submit a request to the [PUT /account/recovery](/docs/references/cloud/client-web/account#updateRecovery) endpoint to complete the process. The verification link sent to the user's email address is valid for 1 hour. \ No newline at end of file diff --git a/docs/references/account/create-session-anonymous.md b/docs/references/account/create-session-anonymous.md index f10778b786..09f091df5a 100644 --- a/docs/references/account/create-session-anonymous.md +++ b/docs/references/account/create-session-anonymous.md @@ -1 +1 @@ -Use this endpoint to allow a new user to register an anonymous account in your project. This route will also create a new session for the user. To allow the new user to convert an anonymous account to a normal account, you need to update its [email and password](/docs/client/account#accountUpdateEmail) or create an [OAuth2 session](/docs/client/account#accountCreateOAuth2Session). \ No newline at end of file +Use this endpoint to allow a new user to register an anonymous account in your project. This route will also create a new session for the user. To allow the new user to convert an anonymous account to a normal account, you need to update its [email and password](UpdateEmail) or create an [OAuth2 session](/docs/references/cloud/client-web/account#CreateOAuth2Session). \ No newline at end of file diff --git a/docs/references/account/create.md b/docs/references/account/create.md index 84e0008153..5278939479 100644 --- a/docs/references/account/create.md +++ b/docs/references/account/create.md @@ -1 +1 @@ -Use this endpoint to allow a new user to register a new account in your project. After the user registration completes successfully, you can use the [/account/verfication](/docs/client/account#accountCreateVerification) route to start verifying the user email address. To allow the new user to login to their new account, you need to create a new [account session](/docs/client/account#accountCreateSession). \ No newline at end of file +Use this endpoint to allow a new user to register a new account in your project. After the user registration completes successfully, you can use the [/account/verfication](/docs/references/cloud/client-web/account#createVerification) route to start verifying the user email address. To allow the new user to login to their new account, you need to create a new [account session](/docs/references/cloud/client-web/account#createSession). \ No newline at end of file diff --git a/docs/references/account/delete-session.md b/docs/references/account/delete-session.md index c7439638af..220147ab13 100644 --- a/docs/references/account/delete-session.md +++ b/docs/references/account/delete-session.md @@ -1 +1 @@ -Logout the user. Use 'current' as the session ID to logout on this device, use a session ID to logout on another device. If you're looking to logout the user on all devices, use [Delete Sessions](/docs/client/account#accountDeleteSessions) instead. \ No newline at end of file +Logout the user. Use 'current' as the session ID to logout on this device, use a session ID to logout on another device. If you're looking to logout the user on all devices, use [Delete Sessions](/docs/references/cloud/client-web/account#deleteSessions) instead. \ No newline at end of file diff --git a/docs/references/account/update-magic-url-session.md b/docs/references/account/update-magic-url-session.md index b85036082f..ea3d10c3b2 100644 --- a/docs/references/account/update-magic-url-session.md +++ b/docs/references/account/update-magic-url-session.md @@ -1,3 +1,3 @@ -Use this endpoint to complete creating the session with the Magic URL. Both the **userId** and **secret** arguments will be passed as query parameters to the redirect URL you have provided when sending your request to the [POST /account/sessions/magic-url](/docs/client/account#accountCreateMagicURLSession) endpoint. +Use this endpoint to complete creating the session with the Magic URL. Both the **userId** and **secret** arguments will be passed as query parameters to the redirect URL you have provided when sending your request to the [POST /account/sessions/magic-url](/docs/references/cloud/client-web/account#createMagicURLSession) endpoint. Please note that in order to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface. \ No newline at end of file diff --git a/docs/references/account/update-phone-session.md b/docs/references/account/update-phone-session.md index 7bd8e61617..3f8866d953 100644 --- a/docs/references/account/update-phone-session.md +++ b/docs/references/account/update-phone-session.md @@ -1 +1 @@ -Use this endpoint to complete creating a session with SMS. Use the **userId** from the [createPhoneSession](/docs/client/account#accountCreatePhoneSession) endpoint and the **secret** received via SMS to successfully update and confirm the phone session. \ No newline at end of file +Use this endpoint to complete creating a session with SMS. Use the **userId** from the [createPhoneSession](/docs/references/cloud/client-web/account#createPhoneSession) endpoint and the **secret** received via SMS to successfully update and confirm the phone session. \ No newline at end of file diff --git a/docs/references/account/update-phone.md b/docs/references/account/update-phone.md index 934d9cad7f..fab28fbf5a 100644 --- a/docs/references/account/update-phone.md +++ b/docs/references/account/update-phone.md @@ -1 +1 @@ -Update the currently logged in user's phone number. After updating the phone number, the phone verification status will be reset. A confirmation SMS is not sent automatically, however you can use the [POST /account/verification/phone](/docs/client/account#accountCreatePhoneVerification) endpoint to send a confirmation SMS. \ No newline at end of file +Update the currently logged in user's phone number. After updating the phone number, the phone verification status will be reset. A confirmation SMS is not sent automatically, however you can use the [POST /account/verification/phone](/docs/references/cloud/client-web/account#createPhoneVerification) endpoint to send a confirmation SMS. \ No newline at end of file diff --git a/docs/references/avatars/get-browser.md b/docs/references/avatars/get-browser.md index 616439a470..8c1508a939 100644 --- a/docs/references/avatars/get-browser.md +++ b/docs/references/avatars/get-browser.md @@ -1,3 +1,3 @@ -You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET /account/sessions](/docs/client/account#accountGetSessions) endpoint. Use width, height and quality arguments to change the output settings. +You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET /account/sessions](/docs/references/cloud/client-web/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings. When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px. \ No newline at end of file diff --git a/docs/references/teams/create-team-membership.md b/docs/references/teams/create-team-membership.md index ffa52b83c2..d1ae048af9 100644 --- a/docs/references/teams/create-team-membership.md +++ b/docs/references/teams/create-team-membership.md @@ -2,6 +2,6 @@ Invite a new member to join your team. Provide an ID for existing users, or invi You only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters. -Use the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow the user to accept the invitation to the team. +Use the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](/docs/references/cloud/client-web/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. Please note that to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console. diff --git a/docs/references/teams/get-team-prefs.md b/docs/references/teams/get-team-prefs.md index 25837f00e1..3cc4721f34 100644 --- a/docs/references/teams/get-team-prefs.md +++ b/docs/references/teams/get-team-prefs.md @@ -1 +1 @@ -Get the team's shared preferences by its unique ID. If a preference doesn't need to be shared by all team members, prefer storing them in [user preferences](/docs/client/account#accountGetPrefs). \ No newline at end of file +Get the team's shared preferences by its unique ID. If a preference doesn't need to be shared by all team members, prefer storing them in [user preferences](/docs/references/cloud/client-web/account#getPrefs). \ No newline at end of file diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 5cfdeeeecb..ff86de6b76 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -183,7 +183,7 @@ - BREAKING Renamed users.deleteUser to users.delete - BREAKING Renamed parameter inviteId to membershipId on teams.updateMembershipStatus, teams.deleteMembership - JWT Support client.setJWT('JWT_GENERATED_IN_CLIENT') -- [Update membership roles](https://appwrite.io/docs/client/teams?sdk=dart#teamsUpdateMembershipRoles) +- [Update membership roles](https://appwrite.io/docs/references/cloud/server-dart/teams?sdk=dart#updateMembershipRoles) - New awesome image preview features, supports borderRadius, borderColor, borderWidth ## 0.5.0-dev.1 diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index ff079b82cc..6c6c7f9c9d 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -209,11 +209,11 @@ https://github.com/appwrite/appwrite/blob/master/CHANGES.md - Upgraded to Null-safety, minimum Dart SDK required 2.12.0 - Upgraded all underlying dependencies to null safe version - BREAKING Renamed parameter inviteId to membershipId on teams.updateMembershipStatus, teams.deleteMembership -- [Anonymous login](https://appwrite.io/docs/client/account?sdk=flutter#accountCreateAnonymousSession) -- [JWT Support](https://appwrite.io/docs/client/account?sdk=flutter#accountCreateJWT) +- [Anonymous login](https://appwrite.io/docs/references/cloud/client-flutter/account?sdk=flutter#createAnonymousSession) +- [JWT Support](https://appwrite.io/docs/references/cloud/client-flutter/account?sdk=flutter#createJWT) - Fallback Cookies for Flutter Web if 3rd party cookies are blocked - Custom User Agent Support -- [Update membership roles](https://appwrite.io/docs/client/teams?sdk=flutter#teamsUpdateMembershipRoles) +- [Update membership roles](https://appwrite.io/docs/references/cloud/client-flutter/teams?sdk=flutter#updateMembershipRoles) - New awesome image preview features, supports borderRadius, borderColor, borderWidth ## 0.5.0-dev.1 diff --git a/docs/services/account.md b/docs/services/account.md index 746493381f..7f337be136 100644 --- a/docs/services/account.md +++ b/docs/services/account.md @@ -1,8 +1,8 @@ The Account service allows you to authenticate and manage a user account. You can use the account service to update user information, retrieve the user sessions across different devices, and fetch the user security logs with his or her recent activity. -Register new user accounts with the [Create Account](/docs/client/account#accountCreate), [Create Magic URL session](/docs/client/account#accountCreateMagicURLSession), or [Create Phone session](/docs/client/account#accountCreatePhoneSession) endpoint. You can authenticate the user account by using multiple sign-in methods available. Once the user is authenticated, a new session object will be created to allow the user to access his or her private data and settings. +Register new user accounts with the [Create Account](/docs/references/cloud/client-web/account#create), [Create Magic URL session](/docs/references/cloud/client-web/account#createMagicURLSession), or [Create Phone session](/docs/references/cloud/client-web/account#createPhoneSession) endpoint. You can authenticate the user account by using multiple sign-in methods available. Once the user is authenticated, a new session object will be created to allow the user to access his or her private data and settings. -This service also exposes an endpoint to save and read the [user preferences](/docs/client/account#accountUpdatePrefs) as a key-value object. This feature is handy if you want to allow extra customization in your app. Common usage for this feature may include saving the user's preferred locale, timezone, or custom app theme. +This service also exposes an endpoint to save and read the [user preferences](/docs/references/cloud/client-web/account#updatePrefs) as a key-value object. This feature is handy if you want to allow extra customization in your app. Common usage for this feature may include saving the user's preferred locale, timezone, or custom app theme. > ## Account API vs Users API > While the Account API operates in the scope of the current logged-in user and usually using a client-side integration, the Users API is integrated from the server-side and operates in an admin scope with access to all your project users. diff --git a/docs/tutorials/add-oauth2-provider.md b/docs/tutorials/add-oauth2-provider.md index 734f199693..b3d81d1194 100644 --- a/docs/tutorials/add-oauth2-provider.md +++ b/docs/tutorials/add-oauth2-provider.md @@ -181,7 +181,7 @@ After you finished adding your new provider to Appwrite, you should be able to s Add credentials and check both a successful and a failed login (where the user denies integration on the provider page). -You can test your OAuth2 provider by trying to login using the [OAuth2 method](https://appwrite.io/docs/client/account#accountCreateOAuth2Session) when integrating the Appwrite Web SDK in a demo app. +You can test your OAuth2 provider by trying to login using the [OAuth2 method](https://appwrite.io/docs/references/cloud/client-web/account#createOAuth2Session) when integrating the Appwrite Web SDK in a demo app. Pass your new adapter name as the provider parameter. If login is successful, you will be redirected to your success URL parameter. Otherwise, you will be redirected to your failure URL. diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 09f39d3169..2d0597dd26 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -68,6 +68,7 @@ abstract class Migration '1.4.1' => 'V19', '1.4.2' => 'V19', '1.4.3' => 'V19', + '1.4.4' => 'V19', ]; /** diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 423dd78fe7..739a23aaf4 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -93,6 +93,12 @@ class Doctor extends Action Console::log('🟢 HTTPS force option is enabled'); } + if ('enabled' !== App::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled')) { + Console::log('🔴 HTTPS force option is disabled for function domains'); + } else { + Console::log('🟢 HTTPS force option is enabled for function domains'); + } + $providerName = App::getEnv('_APP_LOGGING_PROVIDER', ''); $providerConfig = App::getEnv('_APP_LOGGING_CONFIG', ''); diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 975f24ccf5..3eea7b7b7e 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Database\Validator\Queries; +use Appwrite\Extend\Exception; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 36f6ad0dc5..d81a4048c2 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -70,7 +70,7 @@ class Executor array $variables = [], string $command = null, ) { - $runtimeId = "$projectId-$deploymentId"; + $runtimeId = "$projectId-$deploymentId-build"; $route = "/runtimes"; $params = [ 'runtimeId' => $runtimeId, @@ -202,7 +202,9 @@ class Executor 'runtimeEntrypoint' => $runtimeEntrypoint, ]; - $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); + // Safety timeout. Executor has timeout, and open runtime has soft timeout. + // This one shouldn't really happen, but prevents from unexpected networking behaviours. + $timeout = $timeout + 15; $response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); From ce82a9efa905b4d91a414ad3ba538d02185414ae Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 3 Oct 2023 17:41:11 +0300 Subject: [PATCH 054/144] remove usage executions from shared api --- app/controllers/shared/api.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 4afadedbd0..872f2902d1 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -134,13 +134,7 @@ $databaseListener = function (string $event, Document $document, Document $proje ->addMetric(METRIC_DEPLOYMENTS, $value) // per project ->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value)// per function - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value);// per function - - break; - case $document->getCollection() === 'executions': - $queueForUsage - ->addMetric(METRIC_EXECUTIONS, $value) // per project - ->addMetric(str_replace('{functionInternalId}', $document->getAttribute('functionInternalId'), METRIC_FUNCTION_ID_EXECUTIONS), $value);// per function + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value); break; default: break; From 429dabaaeefef785c9f8bee3d515fb4a4412a8b1 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 3 Oct 2023 19:50:48 +0300 Subject: [PATCH 055/144] sync with 1.4 --- app/assets/fonts/inter-v8-latin-regular.woff2 | Bin 0 -> 16608 bytes app/controllers/api/account.php | 14 +- app/controllers/api/functions.php | 4 +- app/controllers/api/health.php | 170 +++++++++++++++--- app/controllers/api/projects.php | 56 +++--- app/controllers/api/storage.php | 2 +- app/controllers/api/users.php | 54 +++--- src/Appwrite/Platform/Workers/Mails.php | 3 +- 8 files changed, 210 insertions(+), 93 deletions(-) create mode 100644 app/assets/fonts/inter-v8-latin-regular.woff2 diff --git a/app/assets/fonts/inter-v8-latin-regular.woff2 b/app/assets/fonts/inter-v8-latin-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..56a570b2480ec79d40d67921b83878ad89b82fa5 GIT binary patch literal 16608 zcmV)XK&`)bPew8T0RR9106^dX5C8xG0Ji7=06>2L0RR9100000000000000000000 z0000Qb{pda9L5F)U_Vn-K~zWpg#-}@3W23ifrfhvg){&GHUcCAiev;J1%ws{APff^ z$)h!Fo7Oyo?tr4u(yysAgLxcej5*V&sF|D;DEt4vCp|@s9bh85jNf{C)JhadXf{3fgSc0-8N|+;% zMFEpmTRR^3=uTr?G6T8@U+DId(pFWDt_yNS{Yrv^9PQU2_ICcO1w0CF9 zFLcvW24Q;2EWAi7UPWG{n4V&ZM>qWmQlLM2(w|UTd5b}^AjJy{CEfI)dtnhTCdlMZ zKC?)9^bs0-r>?c*3#b=dmEU1}RKabYAR$R93U9rwp5d#t&3s7YL<*z`IhgLA_=`vc zSfsP+Hb{2&NHQG6iC_U?X6iuN&FsDG9wNZ#IeSitY#l_&^L+gEJNNx|+MrAl51bQ^ zhlj7V($2dOjysvIC_wLe`|#f!x`nXo0D#2u5iZ8A3$Twyqhy3unSYyF?R@?G6)brY zK8(U%_7znkF8~(JcE?|TzGn32YgQx4AbISV4y-u0C)w+Fmc3wEvMh39I}7A2Lv_dU zqP*zj7@)(7s0J)uUasp1+Iv?t^%do{#Ph$OjQ+WtG{(|Gs`~wVB11-BNycPLmS}0w zkmdQ`CZC5pV2Dw~MPcyDg+)X8@Xh?amR*5-FU{dmQvr$VC_b03^JDLH@2=*gO2il? zGDhlOGu;h~n%E`obAsKkl%a@-1Q97BK|+zi{d?aroK_*P)_UTY9T8;P{|5vFAOipZ zAZkG}q|qMgXdpD$5NMb&(0CJ|X{JFx_yPLMUr;sGpwD~;eRIrcPy?6{L%>8B3&x=X zOeZ5i2tX1p0Xh2w7Y3S3&zb3&PoRn00KY^AtIz{7Z@iDz}ynkY3K(n z{+EI|`-Vc0;xx#dJwwa`jyZM4-+dmVJtNoU>k&{KZ{3^LeI zBaJdzfkIzRaaG2L=XPtM^WmjBv%?-ER zanA#l9;x!!6Hh(!+zT(g^4c4J^DiIyG#Tj1wHwEsyYw*n-|R87bUx~WZPxs>{2QcY zXv%duk-TsM+uCRIgA96V24R6nJC)wG?` z^JwUiBhdqT8WIJ=(e!M^>o;WDl2zW|Xi}MbO_i}A608|Q~EV zKyqlgTCcTSzGe~>^VANoPAyUEN;x+RD#3-d>fHM0LBl7PyEv{>jDPHQgH@&mQ%hPtw3S7 zq2yv+aZDY}=8?dD6tZX75?g6!HhhV@CDUbBTyw*1cidZgU@$6?zL4u{^KHvPQJO(d zOYGCJgD$k4cF}{O&nz8F-f?bl{F{Jx;DyMyA;y~}R3{JIKo5@~f?_y4fhZ{Kmug~M z@*_4!>}|v2>A;kOsbaaqy!&$e%gQU zG2z`)N)?xwo~NX?8Mk9gC7W5M?ELJ6bxy)@ipt!4Bu5=fXL6mp`$CY<_9db~LtBn0 zs%B`$u2aUV>7pg&0JSbxb!SOp8ksW6Xax#m^SJH}K6%9N3=e9%*t09~f?j&nyx!-u zyEq*?=tA3R7d;sIjoDa&!`Tv3+!0Agn^X4k>?=~~E`nk>Jb`$pz!qQvkS(Bf?F$d> z?+c9ZbY?xCbl7?O0qh=9rudm$F(YF%6(}q+Jk8m5rG4RvW`D+J`90@-N~WhJrwHc~ z+q0X0?S^SuQpB;x!FS1&eYb5{%(|&zk+N2MSfu`HQCXQ%&y(LDE9Nz#&nOTGh7a>xVXx68)#8Z|OCna(*eG}0K8upfk zyrbRT(*+;eV?Sb#8tV8ok9wAVA)oT9TY}Q8n=@Y*+Ic(0lI2)|P0jARQ1TCDNV$H> z-R(rk7e|0^`YboRc)GoM};nUx`T1^{mY z*v=wVMItEc!6d6D?A=MqcXiUMqGoH&mRSfQ z5i%*LlZvy_C(XG-%FTw^738W5mBajy(f7p;`6>(D$Qgx7f#V)_$}Hsx8CjTSXHp=J zYPFa6?p?^`IIIzCxJY-h?WX*N@hi*jRtcr&6Sp?rGjL9(dPQZ~9jgg#viY!QzVNL; zF1!i4uUAf0PpfpMG?$k8y7irq&I-yIU22MR?@DByaKJ)^P-f&Mu}hL=aTTPT(jKX= zS*5tO$@6pP6+&apPVreMT+U8J*JE8F<#D``%c_Y%`a0(73mkDOSqD!&pDvA?pcFOh z$6rls#wKo<1NPHQh4&#KZjr`hra%#AHDy{P>6SnQ8MSnnmNCf;L#Z>;^nd40a1!D% zgJN`9mx;49|BtlTPH=K_u7VVrQ-;bbk3zwXsJ=WnU>w24vk-h!{Iu;QeAmJz2I+e%t># zKmwB;o~$&c+%ZayVOXru^qzQlA5w(1?9ZMt$9XZyl6AZ%$KETjkNS)xmyE_hd6>6< zgbC_X5Bgc$?MC6;Ou;YmB9vXea;u8uAwu4gi&hf3Ytt&Zg&xtlJ0mM`YY|^L7}--r zInOES=#?*@G0@)02%TtnEpBJH?d~bWnViezz+74dSsm-DE#uXr1SgppV}@JDN@5eb zq-(6WNeZ}}^8kbxrUGVeMp{x(f6V_br7*j^Lyj+!To}OFBoit1D&|lpmLCwRcIJvw zC&|fzj#`ma-l*}=(vHMGJ~lw@{`_%)b&sZatXnkfu`bdDk9|x-a&lO4rQU$0j8d8t z^O!v6BYS8iiWh@jjacgPI9|D13a6!y%bvZq?ORvHnqlnhtb7axK z>g;bGNlR%C)gQ%pBbsWOu-u+%imEjQCjE0tMoweM`S(f2mn>IXaR^s~M8`9-;depBIy z`HneevD405;*u*?y5@#8eC}B9o(DF0q{>!Lys_P1{-Ino)$I33P7|+h=iK)OM6L_Xb4NvkSGQZXjMln z2};}8IhghshuKv)0W*-Qv{ML1Jlt;E6?3)CvSUHfJa#a%;5klZ* zlWFnBudcY01_~QW!r>y+;hsej_Y9HJQ3xbYv5}|PA=Ee`T%#{P9!ey^rv0Tz<1z2!OW!yAO)%v1Vl5uprj@5?_U@?8s8(2 zXYPlYKrr~04ge7hhj-9Kqxx+Z&OfI!!FC}%BMp-OFV11_f(Dl|$aLs=B_}Nl`d#}R zLf)-6503=E)TIc{hfyd1KNLrxq87!j)^8_mu8r5OO?Gzd8<0C8a_ij=JBHcYK^nCL_ybE<`8k0r%`k(N;mS)V>TXhbC}6tE{5q0vs3g^%tnwNGoe(P8~_)KgPpw__@UBm zSv+98dj`O_UjfM`U9}@ITmsF{m75qxQtJR%MglD_n;cr61(((;K$Xg-2_9~Nk4**e z8VB0T@f9H=1YntQq+`A#vr6%z7)SaG+ePI7@YcqW;lj&EW&sa3bl^-8!mB;Hqq+1+=rZqR?91|fBR{y zI+`?UFc5;%s)_(zX_ofH0t?9le!3>vGbfzCFMQ?O0R{_7tne-UMMac>x zbb!ejbB3}kdQZ8(xM58g(ZH;N;1Y-HZ0xY%*-?&KIj_?__vgu*n{WoOIb8 zPyFeF1Z6a+Xw_u{n;K%AR2de?v(8pU4td2{7hUm*3fJ9m`_M`~|87(y>zq-s(Pok) zRaI3101y$8mc12k#+Jo2wDg9*BT{SqYRC0lKGie{Bm*vOK5pk0m`MWRFeyorBme+F z<2;Ju*y7DMF~$%YdoLpRi2j4Y43UXpl(CL$8cfq6M;>?b8Rs(0XcJ5|%UtI+FlMqD z=3qP3u4%*9vB{gU@Up9Kxb?34SYfSAw%h$a_qA+;t#$z@(<2noNI*JrfJ20h6g^I$ zpuj_c31@uqgF=E1J2p72`MxZ}O=bK;_FZc_wVFi4SAE;Ue7k&@*q*QkpaX$n+n(N#Br zj4};6a2K^UIou8>JHxP9>keIe^}~WKC+@uW(=RgzuDpa)rB7(0laTb}1dkXyY5JT( zMTwUpQ_lJ37nLMkc5RYIGS*UC-ECB`bk+JTJN8tfLX8G3I&|NAZ~av?Xwjj&k6Kde z-t?BYe!{1I#)D73_~wUS{`jXSUU==D2S{?NziKxsl6B6gNIDc46&r0MkAC%BNPD9vnasH zEZ`^(=Q6uWrxz~(sG0S&=5#>|1SwdV%g|3iBccliOG+>ycD;$<$!QAYOI(TMo(4dR}0E9H`t-vp@e0ScXZ3^bCJ*Mi!uAmKIqf@%B0&AUqaDE8bUtg zLq6c^9t=rKLm;ow9YtEkw54f5VIeT!R_#FszqrFpT5q+Tk-liR9N1n9p>!2paLG-+ zn~ezmTMZAOp;9Y&Tve^rblXa`M7|=gcuR#FA;ggA%!x1~nafHPg`&absEmrLlq#vF z8mjqvRt7punFS5d5ixEE2)WJ-xl?1qHyt-DS=V((&*)K|(aU>hAMY#uHz)w4N0iEg z03slC1cjzTdZ@J*Zb1VUcfoC_`Qj$FUCI&*tw0fbG~}egUVF;8;!bSz5l0~ufj*hZ zX?r{kHVXg%6pKq&(^?KTty{xIS&vemT?5w7rk0Ncjr5r{hMt8Mrp{J#!ti=R{Am)9%*NKTNyeKm*+#LmH|@J*uhDe9 zKa2I8UyrjSHYRo*T6MC$%>=u8xOGBq)iAF*jYApjTT~XLLy`E%FjU0gp$%vxHN100 zycQG8fx5FiJ9PjED#Z{0!;BHH$T(>z(iq7COBSRt^wdpQcQu0q005MlaZI+VRjEp?*d@WTqvh5B zQn$cT)(GA}P2Y$yMUW}BGXc?X8%U<#H)G8PbZ`MkQISn;N)04q_GSR7bhWptDo@nC z0Hn#1v$CKRoR^kn0O=`!+t>sB-Whizub)KJ8yZ{!J*aL1&>&4IHPpzw#XyXyp~Z^; z-=>^trn9bF9ADpcQ{*D{*{8x{hXe*d6d7!2U4U3-41j1OmYJm$12HhhKGI0zjXVU1 zfmYC)wMJ`49rZRaR!o;eSfiys4FFpjyM=`{y>v86D!yc~61(TK?w3|S%3wWL0ig-w%I}D$lETv;0C{>n2jO(uS0VdNH(*4qE$HTh+_!iJF@^BcZx*9 z^5lK*2S57B&wlZ%-^_B{2`8OGuAS#Utu*OvfrZL`y#cxxMi zs{rJqt^rX1hOy7K_UHUV!Aseo+H|-OKzflr0G|N>JP`mWWLTa7p#LI-KXP~9(wHi` z0EL&*nJQ=yqnF_Z66h>|yaRy3;{yc%zWv~4FcQac6Isds&YMcXn8Zmj)whecL#8vI zXOUlZl$o+nPAVoPRIw)WqgzqWrgA#-GQyBMJIv8AkG3Cr27MII(EWG^oNPk$FD8k7p4 zvj+elD!}etT<1n{#2mj{XM8M~t(<9BSLLz_TO785DP;IHE}GXvOvSfVrV&7|Hp4xq zSnn6ZD)y&8-4ptU>Bam8PL|4K$LeiKd##xu-SMMv;Yk>mwJV_i#-w z!wfgV5U_FfVvp80k^IkI`y7O@f3pDqxC#J}*8tuI^b-K)w*cHpfQlryL3F zb_hSkWlEYhoFN}V#me(a?fMAz);#W^O-<1UIIP8d13`@e$W+g+6LE*chQxL3nhn ztti$$#oGQhT^x9mrgwyD(zzwEacWj}73Br)6)%DwHcj)}U{Og}1my@1^^8UnSN zWsKS@mbpcR+>v-$7K2uu!mjvBP$NbBszj9ld0@QlZWL!%Q= zeD2%@N$yrwTgSfdcg9Gb`LT1u>~}N{Q>;XVnZDt>s+c=lT_>>Iy+etV+t2{42<8#9 znEO<+uX-g{6EC6^8Vo~q5)J~~RlLh5w^0qLn;bC;0@U&v(LhC9>@ksb#?Q1gQ}09L zb4t+yvEpUzafoT$;kaS8McpfU=EtYEq9S6S`totbx+HfwGKW{&eK*|R!-ax(rdpwt zzVp~RiqD+whm}J#n{Ar8T&kY9yN@|p=8Q)TA5!5>JkF*X7*W*G)HSj}=XDU&D^Usz ziW@^QxIe+G>#HB`fEPXS$`vsUV9+)R$h8LE3pZ{936(KGkR8I|hy=GUG{S?xu9f)< zxVQBo5SA}Po^XOS}NfA<3@^J-;c3Ru& zgymBUD84dQ9?};zQn63U{6`?_v4bHSC(=)DgCIEfwmHznXH)&y6fBd;D=@yqtpU?2 zK-W+p)*(nhBaqO7#ANA?v-C*oNp3BKS@zI!@y^b!#=4eW;3b#eMwR(sl zSO1pBPDi>>(1Nedwj`Kik}W6uGm!(5?@M|U8i>VXSCvT+N;?|(+@bNaT~X>`f~6Dm zrowgU*JMdlsw^elYZU0hsjRxr(13SB=Lk?G1DYjG`M65q6{+Wiga!eOHopcfJNIWF zf!KaT^zb)DPJuLs8kErUU*RHED)vs|wps#M@!JwzyP%q+;O>63vupNt%>Lf)RQt(d zN<&jr!N^Xwg!QBn`UjmexW_}X)F-Hut@(0e*c{fnSG%vZXy{@Z*JHcTq%&0(I+S6MbHJAy5oIXEt;obh#R8zeWdt};a}nYZ;kxCd_Gq`M#wdU<1f>B?DCO<3e;md9Ua z#w(kMA+&()C#q||^O7M()RCkll!ov!^)7U4FLc79kn9{Qd2Q%Mo&NR9bX{88MC&X4 zgoKQX2rlLbhQaYftsJ6~x`9TSzQ`UY24Nia)duUZXC-7)#F&n&I?O#*aXMhOcO>lY zg0fC+=4EQ#+tMb3SE+!&p~QFm-I@XA4N2Z&)gXgYYa00LS-}@v&3>NlHMO}rwS)Bq zQhOqRBz~pllvqh1;bD31Dkfto-_Gn-v1!-9SHKq5!F>&Ww+kdZ^}3c~hrZXcMry}K z6L<&+Q7J2Nqn)@V?fdkkb z#lF&x*Ikl*M#DaY2d)9z#67(4n+}_=g@?{I(NTKe))&s^$ZkDDm~ib;X%9wechc3) za{Z1gSk`Kt#t_Lj6GnldO513-VVXl~Hz~=^YnVrSPj@iClCQ57t8+TDo~^8=YqMxa zJ_<6!-=uN!VzjnrWeyla!xYg(Bd_f8f+GlcdQx`1kD(L_H=PscT8Jbi2#E$-Q$E{)O!N+G-J6Z*V)}bhk1F2T+nM=GLmJLpce9giiHEI z2a=0!IZr+Vb+r>L_o0?_a}hT@JnaG1rc=Q96B`w)rK-gB)o-Nm<#eLu1Xm73;<}Bs zZ)b14;eU1Vlv*Wxw_5Ov>!naucwIZ-?HAb!7nvyKzzSJKjwMQF<0|)&R6{9*hX8O@ z)TfmuhuS$DtuXi>$Z8+=vO{ZS3p5^41XAr#p9K5zKs0<^vO$TB3t41&!ZD}v<%HrH z{Yf>_tpNvYL~=GT<@| ztOw%ukwoxN!hE!Wt!Op_O|I?8b2Gx*x=C1mR6(A_R>^P4wVi}|OfczJ!^VTA4U^bA zKo4DH7a)Imp8AxYgf7o=#1Eh_@(klmdo!M^bB_L=IiSRuAs>H@?gvS3*eV7400Ps=nr&X)YK1Kr$ zVTA$^?`(v{iD|Tnm%vbN*ic}{{InZ^;O0Y!!Ig`N@o|eG{C!M3rHIKqmet?S3i9W2 z{eyytg9qIr15Y0h4m);?+VR8d8eos3Ai*8v@-cy2Fs_uZ0n)t5e!*PqC0~@o7NB71 zk$ywP=Vo5qU^6+=0E1WmA+Pj>sJ;5r;nN@9t|~t+O;^y8N2Dp`TThl4W05Ic^optJ zr3aAv9eYbZg?_F*eaewKd1z`RO;ZyX`Xsf*{rKo2{p2kpLE|5*L+I!Zjh5|Szqhww z(KR{*%NI}MXJD`Z1x*KfH(xFucYnP!`uXZA<3E3?IQg%wb5xJnjT&Gmjgoxb%qM1}Nwho$9rh`qT@~ygs`b8NqJxC8nN<4j3+F@~CVXI&-(Y z?WF>H8RgI+?)diZMU7(s(tKPFx|Lvguv}s&m}Nr5I%r@pmH-M128?yck*0COXC52$ zW4CVN2@TwG92D}v39>_!L&bY9=gE##4k;f$BA0SaGelkpZApqoxqhr(BzIk$PDge$ z!>c75J3oE%e>a^9L4sl0R&~IB;{`Y~KtW``u=!aqs2J4g0Kz-*3p* zyB9KJ<_ZgAuHQ`W9Rs8}99LzdUmtV{43(l^9CY;CC&daxdCcCh6*U?aZZxp&e0M}) zDOpU28M)1gy?T?<^Llqk+Pa(DYO-6c6~fD5XUlf)WaX_$MjI(4fLVyEI8wLl7>JRb z94pDW!S5N;I(1elcdxzjuU0R7kzLt*4_i?8GLGBBpk|g%1g3udx9@vzgY9J}!L?2{ zZ(B3=5z!8)3UfIDK+XfC`4$I&47g6Xdopa|^zXj|2fOEYhS$ugnY+Hp8yx(^D_d^{ z%pzE5OdGPi-HZWrGZg&u)jSVlbb@CHhLP-_Xc&x75YSWx3{YUI7KUy#xAcZq*xXdj z8;wTu{oa6BKvaUU8F$(ckAm^H-eb5e9E^?mXfWdBQSNa5mzB_A*6{!I>U+(+B*}UF zy>C-=s&@|W3OekJY7&aY{nr{TLSF>BR4)qN9cP}J-7}V&_w?$faz#5cBXc5siK7V zE)yB3v3N zqu!p*Vb?BIrTtV=Ih?=oxTp@_Y?qeub>M3^v+VxiKM8@eD*{x~?(0>CCK-yq|826i zpm%4SP>ts#qQ2SxLvXy~e`mco-YC;S6itbUgqUw_=1cixRj^~5JaN3;oA}FbLsW-b zD-M2LzQ|Jf+tV{*0UzFd*`|-n;*K# z`>xM550m1GS}>bRK*5^QxGkaNA}RZRowO+tH9SOCmGb$`^Q|O=NJ7z^7l8e&$xrUG zA(B!#M02>cVnWOFQowR_x*st|^bOJ^#QxYioq$ExMcr}$jR(BK3pXOuBh!oqy>9Ej zw9TRBWPyeN>^upBpCUvxR)JI8KZ*2#pz6urM`xWdZSe&o@y(MK7X|`1l-=?BX6Kqm zNbyB2hMQ}kacKV1)*+_08m>;tN&Gm_L;hEKn%_L%LfRGxDVy_EVC*FjC;hxkS^9qqR1y4W&l%N4fU+^K}Jv~WypOez8d>+x4zM!UGr+m~W zCZmq9JeS#|ZKy5R#%R;YB{@i62I(6gwNf)X(luy)`#$Cy=^;RYo3AbMk6!U%sQ`?z z@GP-@wy$kqtO_1hHn-lYbSgj}&3#+beMcR15lY0sM16_q;@p|_4P$Pb;E*ROE@6A>w02?&J2mcI9~T^T%HJ*P=>3_iwN+pFt5|Pb3e&b?498JK zv?U1U_1$-|!%?^%Nwt$~a#U$zh3RX@AFXdLo#Qnx>*S5TwY_aAec`yf@KRxY_goD* zD1)B;)wau3YuUZ7vWA8KGDT~M1=s@h&B5kBG9L46Y(Pr#z_;+qU5sE<>QlHyG4 z_8Gpiy3Y#V8l$d67fo2Wkg}$j zHa(NfX3wOAr)c;Zu4x}*JuhLi?z&klEX zXo^IRdgM}B&tWE!)pVZqMfz{EW3a61~Z{BaXQ9=H>t}J?6`Z^n~pGo+{uZ(_o ztqF80K}mPvy1XYSQ42UGv`^1G=7|aB^r0Y+0b3OSO_1;eH1sXt!s~zn366KKpVQqK zdW&EpV_pliK*;v05R+=BncuYs{BZvy1_PJA}=Gyz{bh!A-IzdEcn2Mo}+z zdJyFFhoh@oo_?2+ij;zUI=tJsR&e@PYS($UC&rm?+;oy+_`*{$go`nm(-3>(Q0zi5 z8+;eADtvL%bsR+QP{Q(`wV@q{BL;M*&ZcQ<5@UP=QG&yjf%W8N8tF?)+vkGqN8j5U z7~}igg-HWPFn)FHbMK=7T;HSPTM7j*bX-7-`+vB;TvhyWfv?HPxhHM<>Cds#ni<*m zuES9*01CE1;~(vhe*bJ!@L){*I+xQndukw_PCa`uJR^6Q5Pv>LIo7TFP0Q$*e#=>v z&di`k^BNbXZ@)#4%fX3lJa1Pq_5msU2T97SyY8RMyB(V?re|ghJthhya!J8>B&~~1 zi*4%(&rF%b^&giz*|5<;2>|^`4=Cje zH2q$BS@aHl7IM&zVePCbkK_@v;R`nNGyO+x#_v2YSuAGWFDqrN$O=mm#HR{4K0R>- zS@#z?fN}Q4piSf<0PD_3LvV06zH_wUf+}QU$%M0mk>G$bE{4DH%TJ1PzS>FcnKjj`$iSw-xlNNA z*%i_McbzR*Nsz~jb~MQ z*!20?f#UFes}W8#PI5qHeMUfTfG5ix8_-bD>JfNUR%CpH+n36@a6josR#m~uPt4}_ z=ec=vS&j9Ni{o0XzE31xJ3t_JUrsDwu97ZI(U9G1OL&!>dfS;}Go*n|a&?w?-M0=j+g% z!w;}5O^dx{z2E$w#ssH$5Ug^S%mjOjL_N-M{s|nw&v7`z!!m8XWE;v2aQ32**ymV> zGt-)RzQ6q-($m=HVUL@a_@UL_o*$2UF7}&#*>lC%p^kf}-e`aH)?`c9iQa>pM6Gb` zd{R>9u}n1%;zkN@+;g+8BJf3B?<^%li~NPyJQ=&H&3I55hA)kw(yJ}a*2=MnhSx97 zA0l0Qo5feflvn+|eXB<4R@6ZH!~J4w7ssnl=WP|HI%4DU?o_|0J$<25Vn0@vFgHxb zWP2&vs^Eb>3Km7D6LU_x7ZTX8)`fUF)u-+9f$@TyyK(=%GR50s=c`wXofN}h9N*1uZ#h;r^?>^kpKr_6G`M3jJ;rl@1NcuRoXC-0N%++<7S61Z>M z+1Z;bm$T&%r6$2BfC5IqNMW$%u<{pja~JXpa_0l9KA#J{Aiu@H^EO`kCkyI3_E^Z# zamG-fTWQkq=(xFzarmkd>KBQHS@~>pcEz|a!KfQ1zAvGDDVNS-S-@(`=Xl|NZ|;;$M+F<=Wdpl+$?JRV0p%@rYR zPyk7duLuHAKonbef=6pR#w3_ z^&l7}ObToxh--OzTtBv;6=-;qpxT{z^GYn0+80HSKj{`_Yac-_QptVtN@U!;x@d1y zT0GV*`oRyTF;vQ6rXS&zs(H721Xf#!hk=yBdP=c!%=BltoJ4S^64g4gQe_ z0mxw=G6+k-ZdZRPC7{JPlwPBTZlhqK#@4->{WhVu;&u^=4IjA4I@^C9zk};Lef&7WAp}Uebac8TbsPC-@9pA!(4$^1V^YLKqL@MxZCE`7MB*pLyp4}r%_Y{ktt#(sP_K1O4C@u14@L=i7m!$`w9|lSf z9xeMz2M6}U@`@cCfWEBotCt7Zfc@FT=LPSzQFZeoEK;x)VB=B<*}{YjhC2f+5v`fX(so$-!cYnU1BGIj}(pA4CR31;-@%c)r!ETE8qETb=n~#B)c! z-{yOCn#&F0A}cl4)!FZOm{XQD&UcSN*Vi6GPHp$4f!em;dj1bz+z`;~vR5A#H~jSP zu7}Ia%%}GYi>vH-?@lkw6S8u$goSigwpfejiUGv~_XqA8yd_(#?~x-Ov_0Y*Ph(qO zZ_qFA>wDn7G=egV5L4&^>CNE>4@T&$LNb}nh8U#z#v$gj!#y2)CdYCA!+p;vy01^X zj6h(y@fxRWpf)DU!+~t$>O~`m{LOuxNIn^x;(8i?3fEYY>1TlOJ?tnZoPoL~dgJDh+}R6M(yUf2GjEdLjE zuMbD<3E{ZED_&1zS%(#d5ts$14rM=XDX@yq0V4{0&~&-`z~}MG5};%txZmY-3{SL* zwTt;anM&to;a3L#U76=C#AiroAs*G$XVMbXUZW@@uvfZK^(40BoJz7>HRuc}Bd0kv zEKHbMK@n0in#f@R#femE16eh6MJ2hQ>rOPg>_%ip8;RcdedUX(# z!uQ!!XPWg>={^@1Ss!Mf3&O43C73o8K^4%_GX`%E;h-+Q=NN}`@>Xq6>KAuA+6SNi zu;cp`E$@_oU>^73B+A5p1W!%rj!x(xrOPjeJ0XY*r&@|4$iBNAhfqJccM};d39kSX`Cs)T;`-7of8t4SC@)G6=0g$@DX{{ zKL&Fey;H1Q1CL-UlgOU*!|s7L*zzRW`$!hi)02gdjLE@!dgc(p6cJX*yFikvc~{Ur z{A`|?{{TK2(8rTd+T~7lbj2SHaDDQ*r42}LLgk3^whO-=^}6lk{7c8}LagRE{- z?uP46@`Vf@xrVQ0Bl6nyt-+Ax5V)2zS3s+AbLeBNjvqA34X>t!WS&`a2~K$I_=(N5 z`91!HNAyw`nHCP;%o~o;`KHLR(1ca{PX#Qy5Akxh$lJJD({K>LVbd9Llb0CmvI~sp zNyd{HGdWW1A|rP4_9`78*+5T@Xu$6v^iqpyz;SQqU5e6p?$QEg8Y>@{F^sQriFq|L1ifkL6<2XlJ1X_n#I5-DHxsB=y zhOcsbqAh%^{VklGPelO+yOig__g;j>zD3YSz8JiEjLL`J)Ob7%kTTsse=SZ*4(EYr z8QFm^IO^k*Xq}0IW+yea#I&{K=Tn6s_aXUtF<`*qK={iP0Qv8@anZInKfigizj}3b zlmPNkqI|b>3w4NRyjeWsgbtz;D>ix0Vt!joGZ_8`aq?Im(>=J~kpHY1$vzh+&me$B z5zooxMBzRZM5#b#8U@|3yJy7%XWUKF5Gjfv{-I@ko(Z4#Qoza9d$Fxnd&(19@o#^X zVwm|`?sA@*-kB8eUgSN{py%ax{7}t$jV*2Y`CrZ&V%8z)6i*V^4R?z1LbkJ4*~@Ow z&w2KizCZokHC2{h9_ZDPr5IA7k(oyU9?pCuY~N)-*am zma+R7j?b`D8RlcVYE+Lw!*XOQG$ZCHb7K9Yzj$3taX3IT*B5$_8Qw!?I1icOF3q6? zy~qK_Bgf+N>^815A~%#hDYQElnPF$&>G)WGb!?7zJVRkhqTWf_+CNB!;4+jAJ-qdJ zhV`&VT%$#xn3({8o$T>0JIFj;OErCh-J2bJk0?z4QFpivbr()+fM(f^2cD+^W7G4j z%gl3vIS&9F9TW2ofH!4_HJKWSil!Z{QM|CUO=l|^*=Fppm0q&VJTh9@4j|*O0Klq1 z3(+F97%f3dg=IlJl8d%2z&0>P+YYd!<}$v90)X;XL3x1y+za*+En$DjM&X%5jbp*a=3z9AQ+1QUd3;av7C(fk%;Bke>l#5T>xj?0$pXnv*@gFUB$8e27v4K zD)99l_!oRe|DkI17QI97(FgPq)lB+iL4&CV1p*`Z4(foP&?J=^V2BcWhd!Vh^vV7& zKtG}XP&Ims-l6yC1Nw++OrP2i<#Rb5G%J4Kz3}Rx8c3M-Zph^?$avR{r`%}%;4Mf@ z1)vxx4W*-os1a(6GEgSUg4u=!_?^_Akjw1FmdOXzua=+AJa19?_0t)j_KQ7(N+d4* zu1BXe&~Ly1{kQG@2H=Ga;0z3mxgBz(#_|Qv*7U2UJJ8q4dI9o}Xm>VH($Fggto?=O z)~stCaPJ9%Zs)<lIyE>-P-9=FuO z&aD@miQZYVVdoLA_OW@JBMNn~oyDBdu@pX}ZP?!$+HiXau$qLy5&>SgHF}&G?H%G; z7Yoa?NYx9`91~y}d|atH*k538z8CBX&OLJ}JFIwibPt_%>7Z*VQPU8-nXNi#Zwh+0 z>K>+rG4MRdf>~yuB_4AMSFDK38hnJaU4{{_Y7!<(0(jw8UBj8t-XX4av9LU|vw@46 z1mF?D*DtP(y5u_IWS4;DQj{QIo}oZ1aDK!JR`IfOw>}C3cHz<`hx$cV0#vG}ULXFN zJ&?@!cSb(}K7@oQc%L|U8uC^*1E|# z!cy%!$uYVUf|=RTJ=A^lY}92-XFz?{t-2uui=bsP`U`a}%{m23UrlZ}oxQ~bmU^|q zy=*n#*p8{WaZ;jF!z!mxH^JbAcz2zN&{k)%C3eAT7s_LYL!6B84n= z3P9M`5?UmIn%q(k!-5wrOKxbsw)M8+Un{E^uvHnkx2 zMaZ}Y{bjGUj+r^)D#@nMAP!`Qh3*(tHG3UM6B4=MhC`WKB0E;P>0{ZDrlwo7S1_z{ z)&RH^q{3h8JK1&+>oBC4rH%OPwdkep^G<-d0<-bYcFnaid9AEj`IiRHvavP(IGaIk zmZ8b-NCB4s`mM0w)eOtZ7{rm_ur=xIoiNpfSaeK0Z7r9U#Wjv%5I+_I#SFYe0B_ed z+EGHfecGvkDd1AGDJ30eXR?Wzt7Nx(+!$QKP97y0S%%*n_Dv9xF@LyZ=aGAn+&kWC zs|^y*<5b?^Q7`g)K7Agp!#KlII(#nVag3#$!9$C~xjfFmP%XY{&Ka(#^dk<&M!~PV ztrt}mwhl%i&Y#*@y*MmxA_tF%7CD&k|g~^BazujHp3M@t0&zWg(II}`I~J301)6R^T_fEw_nZ$Z%*w; z6gCwA%mQ*glFXV2;JDy|xX4avOvvHEf7Z?%GZmw>mY+)PocgLKo|D@^PW?)eEu|f!P=7bFd}W0~%3#BT jIED=#9kY!bEp;)_)x1NUh+!}N?;quQ+j_D+v#bRGZOF+c literal 0 HcmV?d00001 diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 1830886be8..93df84a6f6 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -50,7 +50,7 @@ $oauthDefaultSuccess = '/auth/oauth2/success'; $oauthDefaultFailure = '/auth/oauth2/failure'; App::post('/v1/account') - ->desc('Create Account') + ->desc('Create account') ->groups(['api', 'account', 'auth']) ->label('event', 'users.[userId].create') ->label('scope', 'public') @@ -165,7 +165,7 @@ App::post('/v1/account') App::post('/v1/account/sessions/email') ->alias('/v1/account/sessions') - ->desc('Create Email Session') + ->desc('Create email session') ->groups(['api', 'account', 'auth', 'session']) ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') @@ -437,7 +437,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $provider, string $code, string $state, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); @@ -1110,7 +1110,7 @@ App::put('/v1/account/sessions/magic-url') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + ->action(function (string $userId, string $secret, Request $request, Response $response,Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { /** @var Utopia\Database\Document $user */ @@ -1845,7 +1845,7 @@ App::patch('/v1/account/name') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $name, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $name, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user->setAttribute('name', $name); @@ -1881,7 +1881,7 @@ App::patch('/v1/account/password') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $password, string $oldPassword, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $password, string $oldPassword, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) { // Check old password only if its an existing user. if (!empty($user->getAttribute('passwordUpdate')) && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password @@ -2148,7 +2148,7 @@ App::delete('/v1/account/sessions/:sessionId') ->inject('locale') ->inject('queueForEvents') ->inject('project') - ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Document $project) { + ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Document $project) { $protocol = $request->getProtocol(); $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index b510f37b5f..58ffb0ac90 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -521,7 +521,7 @@ App::get('/v1/functions/:functionId/usage') }); App::get('/v1/functions/usage') - ->desc('Get Functions Usage') + ->desc('Get functions usage') ->groups(['api', 'functions']) ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1001,7 +1001,7 @@ App::post('/v1/functions/:functionId/deployments') ->inject('deviceFunctions') ->inject('deviceLocal') ->inject('queueForBuilds') - ->action(function (string $functionId, string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Build $queueForBuilds) { + ->action(function (string $functionId, mixed $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Build $queueForBuilds) { $activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 0110635052..3b04b8f89a 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -2,8 +2,15 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Audit; +use Appwrite\Event\Build; use Appwrite\Event\Certificate; +use Appwrite\Event\Database; +use Appwrite\Event\Delete; use Appwrite\Event\Event; +use Appwrite\Event\Func; +use Appwrite\Event\Mail; +use Appwrite\Event\Migration; +use Appwrite\Event\Phone; use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; use Utopia\App; @@ -41,7 +48,7 @@ App::get('/v1/health') }); App::get('/v1/health/version') - ->desc('Get Version') + ->desc('Get version') ->groups(['api', 'health']) ->label('scope', 'public') ->label('sdk.response.code', Response::STATUS_CODE_OK) @@ -111,7 +118,7 @@ App::get('/v1/health/db') }); App::get('/v1/health/cache') - ->desc('Get Cache') + ->desc('Get cache') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -168,7 +175,7 @@ App::get('/v1/health/cache') }); App::get('/v1/health/queue') - ->desc('Get Queue') + ->desc('Get queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -225,7 +232,7 @@ App::get('/v1/health/queue') }); App::get('/v1/health/pubsub') - ->desc('Get PubSub') + ->desc('Get pubsub') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -282,7 +289,7 @@ App::get('/v1/health/pubsub') }); App::get('/v1/health/time') - ->desc('Get Time') + ->desc('Get time') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -339,7 +346,7 @@ App::get('/v1/health/time') }); App::get('/v1/health/queue/webhooks') - ->desc('Get Webhooks Queue') + ->desc('Get webhooks queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -349,15 +356,15 @@ App::get('/v1/health/queue/webhooks') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('queueForEvents') + ->inject('queue') ->inject('response') - ->action(function (Event $queueForEvents, Response $response) { - - $response->dynamic(new Document([ 'size' => $queueForEvents->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::WEBHOOK_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize()]), Response::MODEL_HEALTH_QUEUE); + }); App::get('/v1/health/queue/logs') - ->desc('Get Logs Queue') + ->desc('Get logs queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -367,15 +374,15 @@ App::get('/v1/health/queue/logs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('queueForAudits') + ->inject('queue') ->inject('response') - ->action(function (Audit $queueForAudits, Response $response) { - - $response->dynamic(new Document([ 'size' => $queueForAudits->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::AUDITS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); App::get('/v1/health/queue/certificates') - ->desc('Get Certificates Queue') + ->desc('Get certificates queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -385,15 +392,124 @@ App::get('/v1/health/queue/certificates') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('queueForCertificates') + ->inject('queue') ->inject('response') - ->action(function (Certificate $queueForCertificates, Response $response) { + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::CERTIFICATES_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); + +App::get('/v1/health/queue/builds') + ->desc('Get builds queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueueBuilds') + ->label('sdk.description', '/docs/references/health/get-queue-builds.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') + ->inject('response') + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::BUILDS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); + +App::get('/v1/health/queue/databases') + ->desc('Get databases queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueueDatabases') + ->label('sdk.description', '/docs/references/health/get-queue-databases.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') + ->inject('response') + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::CERTIFICATES_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); + +App::get('/v1/health/queue/deletes') + ->desc('Get deletes queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueueDeletes') + ->label('sdk.description', '/docs/references/health/get-queue-deletes.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') + ->inject('response') + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::DELETE_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); + +App::get('/v1/health/queue/mails') + ->desc('Get mails queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueueMails') + ->label('sdk.description', '/docs/references/health/get-queue-mails.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') + ->inject('response') + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::MAILS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); - $response->dynamic(new Document([ 'size' => $queueForCertificates->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); +App::get('/v1/health/queue/messaging') + ->desc('Get messaging queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueueMessaging') + ->label('sdk.description', '/docs/references/health/get-queue-messaging.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') + ->inject('response') + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::MESSAGING_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); + +App::get('/v1/health/queue/migrations') + ->desc('Get migrations queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueueMigrations') + ->label('sdk.description', '/docs/references/health/get-queue-migrations.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') + ->inject('response') + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::MIGRATIONS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); + App::get('/v1/health/queue/functions') - ->desc('Get Functions Queue') + ->desc('Get functions queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -407,11 +523,11 @@ App::get('/v1/health/queue/functions') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::FUNCTIONS_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); + }); App::get('/v1/health/storage/local') - ->desc('Get Local Storage') + ->desc('Get local storage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -454,7 +570,7 @@ App::get('/v1/health/storage/local') }); App::get('/v1/health/anti-virus') - ->desc('Get Antivirus') + ->desc('Get antivirus') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -493,7 +609,7 @@ App::get('/v1/health/anti-virus') }); App::get('/v1/health/stats') // Currently only used internally - ->desc('Get System Stats') + ->desc('Get system stats') ->groups(['api', 'health']) ->label('scope', 'root') // ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index ddcc19f4c7..596702aa76 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -49,7 +49,7 @@ App::init() }); App::post('/v1/projects') - ->desc('Create Project') + ->desc('Create project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -220,7 +220,7 @@ App::post('/v1/projects') }); App::get('/v1/projects') - ->desc('List Projects') + ->desc('List projects') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -267,7 +267,7 @@ App::get('/v1/projects') }); App::get('/v1/projects/:projectId') - ->desc('Get Project') + ->desc('Get project') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -291,7 +291,7 @@ App::get('/v1/projects/:projectId') }); App::patch('/v1/projects/:projectId') - ->desc('Update Project') + ->desc('Update project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -338,7 +338,7 @@ App::patch('/v1/projects/:projectId') }); App::patch('/v1/projects/:projectId/team') - ->desc('Update Project Team') + ->desc('Update project team') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -443,7 +443,7 @@ App::patch('/v1/projects/:projectId/service/all') }); App::patch('/v1/projects/:projectId/oauth2') - ->desc('Update Project OAuth2') + ->desc('Update project OAuth2') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -487,7 +487,7 @@ App::patch('/v1/projects/:projectId/oauth2') }); App::patch('/v1/projects/:projectId/auth/limit') - ->desc('Update Project users limit') + ->desc('Update project users limit') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -518,7 +518,7 @@ App::patch('/v1/projects/:projectId/auth/limit') }); App::patch('/v1/projects/:projectId/auth/duration') - ->desc('Update Project Authentication Duration') + ->desc('Update project authentication duration') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -549,7 +549,7 @@ App::patch('/v1/projects/:projectId/auth/duration') }); App::patch('/v1/projects/:projectId/auth/:method') - ->desc('Update Project auth method status. Use this endpoint to enable or disable a given auth method for this project.') + ->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -676,7 +676,7 @@ App::patch('/v1/projects/:projectId/auth/personal-data') }); App::patch('/v1/projects/:projectId/auth/max-sessions') - ->desc('Update Project user sessions limit') + ->desc('Update project user sessions limit') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -707,7 +707,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions') }); App::delete('/v1/projects/:projectId') - ->desc('Delete Project') + ->desc('Delete project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -742,7 +742,7 @@ App::delete('/v1/projects/:projectId') // Webhooks App::post('/v1/projects/:projectId/webhooks') - ->desc('Create Webhook') + ->desc('Create webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -798,7 +798,7 @@ App::post('/v1/projects/:projectId/webhooks') }); App::get('/v1/projects/:projectId/webhooks') - ->desc('List Webhooks') + ->desc('List webhooks') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -830,7 +830,7 @@ App::get('/v1/projects/:projectId/webhooks') }); App::get('/v1/projects/:projectId/webhooks/:webhookId') - ->desc('Get Webhook') + ->desc('Get webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -864,7 +864,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') }); App::put('/v1/projects/:projectId/webhooks/:webhookId') - ->desc('Update Webhook') + ->desc('Update webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -917,7 +917,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') }); App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') - ->desc('Update Webhook Signature Key') + ->desc('Update webhook signature key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -956,7 +956,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') }); App::delete('/v1/projects/:projectId/webhooks/:webhookId') - ->desc('Delete Webhook') + ->desc('Delete webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -994,7 +994,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') // Keys App::post('/v1/projects/:projectId/keys') - ->desc('Create Key') + ->desc('Create key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1044,7 +1044,7 @@ App::post('/v1/projects/:projectId/keys') }); App::get('/v1/projects/:projectId/keys') - ->desc('List Keys') + ->desc('List keys') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1076,7 +1076,7 @@ App::get('/v1/projects/:projectId/keys') }); App::get('/v1/projects/:projectId/keys/:keyId') - ->desc('Get Key') + ->desc('Get key') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1110,7 +1110,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') }); App::put('/v1/projects/:projectId/keys/:keyId') - ->desc('Update Key') + ->desc('Update key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1156,7 +1156,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') }); App::delete('/v1/projects/:projectId/keys/:keyId') - ->desc('Delete Key') + ->desc('Delete key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1195,7 +1195,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') // Platforms App::post('/v1/projects/:projectId/platforms') - ->desc('Create Platform') + ->desc('Create platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1245,7 +1245,7 @@ App::post('/v1/projects/:projectId/platforms') }); App::get('/v1/projects/:projectId/platforms') - ->desc('List Platforms') + ->desc('List platforms') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1277,7 +1277,7 @@ App::get('/v1/projects/:projectId/platforms') }); App::get('/v1/projects/:projectId/platforms/:platformId') - ->desc('Get Platform') + ->desc('Get platform') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1311,7 +1311,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') }); App::put('/v1/projects/:projectId/platforms/:platformId') - ->desc('Update Platform') + ->desc('Update platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1358,7 +1358,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') }); App::delete('/v1/projects/:projectId/platforms/:platformId') - ->desc('Delete Platform') + ->desc('Delete platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1394,6 +1394,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') $response->noContent(); }); + // CUSTOM SMTP and Templates App::patch('/v1/projects/:projectId/smtp') ->desc('Update SMTP configuration') @@ -1525,6 +1526,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale') $response->dynamic(new Document($template), Response::MODEL_SMS_TEMPLATE); }); + App::get('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Get custom email template') ->groups(['api', 'projects']) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 427f360f78..12fe618462 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1288,7 +1288,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('user') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $bucketId, string $fileId, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { + ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index ec7f82a29e..15e6f21406 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -108,7 +108,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e } App::post('/v1/users') - ->desc('Create User') + ->desc('Create user') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -139,7 +139,7 @@ App::post('/v1/users') }); App::post('/v1/users/bcrypt') - ->desc('Create User with Bcrypt Password') + ->desc('Create user with bcrypt password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -169,7 +169,7 @@ App::post('/v1/users/bcrypt') }); App::post('/v1/users/md5') - ->desc('Create User with MD5 Password') + ->desc('Create user with MD5 password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -199,7 +199,7 @@ App::post('/v1/users/md5') }); App::post('/v1/users/argon2') - ->desc('Create User with Argon2 Password') + ->desc('Create user with Argon2 password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -229,7 +229,7 @@ App::post('/v1/users/argon2') }); App::post('/v1/users/sha') - ->desc('Create User with SHA Password') + ->desc('Create user with SHA password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -266,7 +266,7 @@ App::post('/v1/users/sha') }); App::post('/v1/users/phpass') - ->desc('Create User with PHPass Password') + ->desc('Create user with PHPass password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -296,7 +296,7 @@ App::post('/v1/users/phpass') }); App::post('/v1/users/scrypt') - ->desc('Create User with Scrypt Password') + ->desc('Create user with Scrypt password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -339,7 +339,7 @@ App::post('/v1/users/scrypt') }); App::post('/v1/users/scrypt-modified') - ->desc('Create User with Scrypt Modified Password') + ->desc('Create user with Scrypt modified password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') @@ -372,7 +372,7 @@ App::post('/v1/users/scrypt-modified') }); App::get('/v1/users') - ->desc('List Users') + ->desc('List users') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -420,7 +420,7 @@ App::get('/v1/users') }); App::get('/v1/users/:userId') - ->desc('Get User') + ->desc('Get user') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -445,7 +445,7 @@ App::get('/v1/users/:userId') }); App::get('/v1/users/:userId/prefs') - ->desc('Get User Preferences') + ->desc('Get user preferences') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -472,7 +472,7 @@ App::get('/v1/users/:userId/prefs') }); App::get('/v1/users/:userId/sessions') - ->desc('List User Sessions') + ->desc('List user sessions') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -513,7 +513,7 @@ App::get('/v1/users/:userId/sessions') }); App::get('/v1/users/:userId/memberships') - ->desc('List User Memberships') + ->desc('List user memberships') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -552,7 +552,7 @@ App::get('/v1/users/:userId/memberships') }); App::get('/v1/users/:userId/logs') - ->desc('List User Logs') + ->desc('List user logs') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) @@ -681,7 +681,7 @@ App::get('/v1/users/identities') }); App::patch('/v1/users/:userId/status') - ->desc('Update User Status') + ->desc('Update user status') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.status') ->label('scope', 'users.write') @@ -717,9 +717,9 @@ App::patch('/v1/users/:userId/status') }); App::put('/v1/users/:userId/labels') - ->desc('Update User Labels') + ->desc('Update user labels') ->groups(['api', 'users']) - ->label('event', 'users.[userId].update.verification') + ->label('event', 'users.[userId].update.labels') ->label('scope', 'users.write') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') @@ -754,7 +754,7 @@ App::put('/v1/users/:userId/labels') }); App::patch('/v1/users/:userId/verification/phone') - ->desc('Update Phone Verification') + ->desc('Update phone verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') ->label('scope', 'users.write') @@ -789,7 +789,7 @@ App::patch('/v1/users/:userId/verification/phone') }); App::patch('/v1/users/:userId/name') - ->desc('Update Name') + ->desc('Update name') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.name') ->label('scope', 'users.write') @@ -826,7 +826,7 @@ App::patch('/v1/users/:userId/name') }); App::patch('/v1/users/:userId/password') - ->desc('Update Password') + ->desc('Update password') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.password') ->label('scope', 'users.write') @@ -890,7 +890,7 @@ App::patch('/v1/users/:userId/password') }); App::patch('/v1/users/:userId/email') - ->desc('Update Email') + ->desc('Update email') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.email') ->label('scope', 'users.write') @@ -946,7 +946,7 @@ App::patch('/v1/users/:userId/email') }); App::patch('/v1/users/:userId/phone') - ->desc('Update Phone') + ->desc('Update phone') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.phone') ->label('scope', 'users.write') @@ -989,7 +989,7 @@ App::patch('/v1/users/:userId/phone') }); App::patch('/v1/users/:userId/verification') - ->desc('Update Email Verification') + ->desc('Update email verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') ->label('scope', 'users.write') @@ -1024,7 +1024,7 @@ App::patch('/v1/users/:userId/verification') }); App::patch('/v1/users/:userId/prefs') - ->desc('Update User Preferences') + ->desc('Update user preferences') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.prefs') ->label('scope', 'users.write') @@ -1057,7 +1057,7 @@ App::patch('/v1/users/:userId/prefs') }); App::delete('/v1/users/:userId/sessions/:sessionId') - ->desc('Delete User Session') + ->desc('Delete user session') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('scope', 'users.write') @@ -1100,7 +1100,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') }); App::delete('/v1/users/:userId/sessions') - ->desc('Delete User Sessions') + ->desc('Delete user sessions') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('scope', 'users.write') @@ -1142,7 +1142,7 @@ App::delete('/v1/users/:userId/sessions') }); App::delete('/v1/users/:userId') - ->desc('Delete User') + ->desc('Delete user') ->groups(['api', 'users']) ->label('event', 'users.[userId].delete') ->label('scope', 'users.write') diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 9db6594a1b..b0cf28caeb 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -57,8 +57,7 @@ class Mails extends Action $subject = $payload['subject']; $variables = $payload['variables']; $name = $payload['name']; - - $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); + $body = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-base.tpl'); foreach ($variables as $key => $value) { $body->setParam('{{' . $key . '}}', $value); From e3668a4d06248693649d4f4941a904bd33206477 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 3 Oct 2023 21:56:36 +0300 Subject: [PATCH 056/144] sync with 1.4 --- app/controllers/api/databases.php | 62 +++++++++++---------- src/Appwrite/Platform/Workers/Audits.php | 1 + src/Appwrite/Platform/Workers/Databases.php | 2 +- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 06fc5254fe..a16b8116f8 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -20,6 +20,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; +use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; @@ -227,7 +228,7 @@ function updateAttribute( string $collectionId, string $key, Database $dbForProject, - Event $events, + Event $queueForEvents, string $type, string $filter = null, string|bool|int|float $default = null, @@ -371,7 +372,7 @@ function updateAttribute( $attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collection->getId()); - $events + $queueForEvents ->setContext('collection', $collection) ->setContext('database', $db) ->setParam('databaseId', $databaseId) @@ -1803,7 +1804,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $queueForEvents, + queueForEvents: $queueForEvents, type: Database::VAR_STRING, default: $default, required: $required @@ -1836,14 +1837,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email ->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_STRING, filter: APP_DATABASE_ATTRIBUTE_EMAIL, default: $default, @@ -1878,14 +1879,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/ ->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_STRING, filter: APP_DATABASE_ATTRIBUTE_ENUM, default: $default, @@ -1920,14 +1921,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k ->param('default', null, new Nullable(new IP()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_STRING, filter: APP_DATABASE_ATTRIBUTE_IP, default: $default, @@ -1961,14 +1962,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/: ->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_STRING, filter: APP_DATABASE_ATTRIBUTE_URL, default: $default, @@ -2004,14 +2005,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_INTEGER, default: $default, required: $required, @@ -2055,14 +2056,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_FLOAT, default: $default, required: $required, @@ -2104,14 +2105,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole ->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_BOOLEAN, default: $default, required: $required @@ -2144,14 +2145,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet ->param('default', null, new Nullable(new DatetimeValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) { + ->inject('queueForEvents') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, key: $key, dbForProject: $dbForProject, - events: $events, + queueForEvents: $queueForEvents, type: Database::VAR_DATETIME, default: $default, required: $required @@ -2183,7 +2184,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ ->param('onDelete', null, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true) ->inject('response') ->inject('dbForProject') - ->inject('events') + ->inject('queueForEvents') ->action(function ( string $databaseId, string $collectionId, @@ -2191,14 +2192,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ ?string $onDelete, Response $response, Database $dbForProject, - Event $events + Event $queueForEvents ) { $attribute = updateAttribute( $databaseId, $collectionId, $key, $dbForProject, - $events, + $queueForEvents, type: Database::VAR_RELATIONSHIP, required: false, options: [ @@ -2934,6 +2935,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $cursor = \reset($cursor); + if ($cursor) { $documentId = $cursor->getValue(); diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 038cad13ef..53c16d8eb3 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -71,6 +71,7 @@ class Audits extends Action ip: $ip, location: '', data: [ + 'userId' => $user->getId(), 'userName' => $userName, 'userEmail' => $userEmail, 'mode' => $mode, diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 9e9c0126ad..6ff3a5d48b 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -213,7 +213,7 @@ class Databases extends Action * @throws Conflict * @throws Exception **/ - private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject) + private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void { $projectId = $project->getId(); From 7f1507f8108b17ebb11739399825dfa13df8db47 Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 4 Oct 2023 11:20:32 +0300 Subject: [PATCH 057/144] sync with 1.4 --- .env | 6 ++- app/controllers/api/databases.php | 3 +- app/controllers/api/users.php | 6 +-- app/init.php | 5 +- composer.lock | 52 ++++++++++--------- docker-compose.yml | 35 ++----------- .../Databases/DatabasesConsoleClientTest.php | 47 ++++++++++++++--- .../Functions/FunctionsConsoleClientTest.php | 13 +++-- .../Projects/ProjectsConsoleClientTest.php | 16 +++--- .../Storage/StorageConsoleClientTest.php | 15 +++--- 10 files changed, 103 insertions(+), 95 deletions(-) diff --git a/.env b/.env index e6c1a68108..feeb74d849 100644 --- a/.env +++ b/.env @@ -9,7 +9,9 @@ _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= _APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ROUTER_PROTECTION=disbled _APP_OPTIONS_FORCE_HTTPS=disabled +_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled _APP_OPENSSL_KEY_V1=your-secret-key _APP_DOMAIN=localhost _APP_DOMAIN_FUNCTIONS=functions.localhost @@ -57,8 +59,8 @@ _APP_SMTP_PORT=1025 _APP_SMTP_SECURE= _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= -_APP_SMS_PROVIDER= -_APP_SMS_FROM= +_APP_SMS_PROVIDER=sms://username:password@mock +_APP_SMS_FROM=+123456789 _APP_STORAGE_LIMIT=30000000 _APP_STORAGE_PREVIEW_LIMIT=20000000 _APP_FUNCTIONS_SIZE_LIMIT=30000000 diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a16b8116f8..10fd5265f9 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -17,10 +17,11 @@ use MaxMind\Db\Reader; use Utopia\App; use Utopia\Audit\Audit; use Utopia\Config\Config; +use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Database; +use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; -use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 15e6f21406..99322f5bad 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -258,7 +258,7 @@ App::post('/v1/users/sha') $options = '{"version":"' . $passwordVersion . '"}'; } - $user = createUser('sha', $options, $userId, $email, $password, null, $name, $project, $dbForProject, $events); + $user = createUser('sha', $options, $userId, $email, $password, null, $name, $project, $dbForProject, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -1199,9 +1199,7 @@ App::delete('/v1/users/identities/:identityId') ->param('identityId', '', new UID(), 'Identity ID.') ->inject('response') ->inject('dbForProject') - ->inject('events') - ->inject('deletes') - ->action(function (string $identityId, Response $response, Database $dbForProject, Event $events, Delete $deletes) { + ->action(function (string $identityId, Response $response, Database $dbForProject) { $identity = $dbForProject->getDocument('identities', $identityId); diff --git a/app/init.php b/app/init.php index 46d90cf124..f3084c3cdb 100644 --- a/app/init.php +++ b/app/init.php @@ -69,6 +69,7 @@ use Utopia\Pools\Group; use Utopia\Pools\Pool; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; +use Appwrite\Auth\OAuth2\Github; use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Func; @@ -109,8 +110,8 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 510; -const APP_VERSION_STABLE = '1.4.3'; +const APP_CACHE_BUSTER = 511; +const APP_VERSION_STABLE = '1.4.4'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/composer.lock b/composer.lock index d03e72e6e0..5c1e3c9a9b 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": "509d6d460bad65092c5e3b8d6b390ec9", + "content-hash": "441bddff081659dfec29b0d735a6217a", "packages": [ { "name": "adhocore/jwt", @@ -658,16 +658,16 @@ }, { "name": "matomo/device-detector", - "version": "6.1.5", + "version": "6.1.6", "source": { "type": "git", "url": "https://github.com/matomo-org/device-detector.git", - "reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4" + "reference": "5cbea85106e561c7138d03603eb6e05128480409" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/40ca2990dba2c1719e5c62168e822e0b86c167d4", - "reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", + "reference": "5cbea85106e561c7138d03603eb6e05128480409", "shasum": "" }, "require": { @@ -723,7 +723,7 @@ "source": "https://github.com/matomo-org/matomo", "wiki": "https://dev.matomo.org/" }, - "time": "2023-08-17T16:17:41+00:00" + "time": "2023-10-02T10:01:54+00:00" }, { "name": "mongodb/mongodb", @@ -1433,16 +1433,16 @@ }, { "name": "utopia-php/database", - "version": "0.43.2", + "version": "0.43.4", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "f2626acd42665a9987c94af1c93bf20c28d55c9d" + "reference": "cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/f2626acd42665a9987c94af1c93bf20c28d55c9d", - "reference": "f2626acd42665a9987c94af1c93bf20c28d55c9d", + "url": "https://api.github.com/repos/utopia-php/database/zipball/cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c", + "reference": "cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c", "shasum": "" }, "require": { @@ -1483,9 +1483,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.43.2" + "source": "https://github.com/utopia-php/database/tree/0.43.4" }, - "time": "2023-09-07T19:04:33+00:00" + "time": "2023-09-28T09:00:05+00:00" }, { "name": "utopia-php/domains", @@ -2270,16 +2270,16 @@ }, { "name": "utopia-php/storage", - "version": "0.14.0", + "version": "0.17.0", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", "shasum": "" }, "require": { @@ -2287,10 +2287,12 @@ "ext-fileinfo": "*", "ext-lz4": "*", "ext-snappy": "*", + "ext-xz": "*", "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2317,9 +2319,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" + "source": "https://github.com/utopia-php/storage/tree/0.17.0" }, - "time": "2023-03-15T00:16:34+00:00" + "time": "2023-08-21T11:28:36+00:00" }, { "name": "utopia-php/swoole", @@ -2714,16 +2716,16 @@ }, { "name": "doctrine/deprecations", - "version": "v1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { @@ -2755,9 +2757,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2023-06-03T09:27:29+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/instantiator", diff --git a/docker-compose.yml b/docker-compose.yml index 10dfd0b96e..51608a444c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -108,7 +108,9 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET @@ -221,6 +223,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -267,10 +270,6 @@ services: - _APP_DB_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE appwrite-worker-webhooks: entrypoint: worker-webhooks @@ -298,10 +297,6 @@ services: - _APP_REDIS_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE appwrite-worker-deletes: entrypoint: worker-deletes @@ -360,10 +355,6 @@ services: - _APP_LOGGING_CONFIG - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE appwrite-worker-databases: entrypoint: worker-databases @@ -391,10 +382,6 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG @@ -439,7 +426,9 @@ services: - _APP_FUNCTIONS_BUILD_TIMEOUT - _APP_FUNCTIONS_CPUS - _APP_FUNCTIONS_MEMORY + - _APP_FUNCTIONS_SIZE_LIMIT - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_DOMAIN - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY @@ -462,10 +451,6 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE appwrite-worker-certificates: entrypoint: worker-certificates @@ -499,10 +484,6 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG @@ -533,10 +514,6 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE - _APP_FUNCTIONS_TIMEOUT - _APP_FUNCTIONS_BUILD_TIMEOUT - _APP_FUNCTIONS_CPUS @@ -573,7 +550,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_CONNECTIONS_QUEUE - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -602,7 +578,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_CONNECTIONS_QUEUE - _APP_SMS_PROVIDER - _APP_SMS_FROM - _APP_LOGGING_PROVIDER diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index 04ac00c263..1476c8b28a 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -193,6 +193,44 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals($response['body'], ""); } + /** + * @depends testCreateCollection + */ + public function testGetDatabaseUsage(array $data) + { + $databaseId = $data['databaseId']; + /** + * Test for FAILURE + */ + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(count($response['body']), 3); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['documentsTotal']); + $this->assertIsArray($response['body']['collectionsTotal']); + } + + /** * @depends testCreateCollection */ @@ -230,15 +268,10 @@ class DatabasesConsoleClientTest extends Scope ], $this->getHeaders()), [ 'range' => '24h' ]); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(count($response['body']), 6); + $this->assertEquals(count($response['body']), 2); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['documentsCount']); - $this->assertIsArray($response['body']['documentsCreate']); - $this->assertIsArray($response['body']['documentsRead']); - $this->assertIsArray($response['body']['documentsUpdate']); - $this->assertIsArray($response['body']['documentsDelete']); + $this->assertIsArray($response['body']['documentsTotal']); } /** diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index b458a4316b..3dad6cb386 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -92,16 +92,15 @@ class FunctionsConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 9); + $this->assertEquals(count($response['body']), 8); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['executionsTotal']); - $this->assertIsArray($response['body']['executionsFailure']); - $this->assertIsArray($response['body']['executionsSuccess']); - $this->assertIsArray($response['body']['executionsTime']); + $this->assertIsArray($response['body']['deploymentsTotal']); + $this->assertIsArray($response['body']['deploymentsStorage']); $this->assertIsArray($response['body']['buildsTotal']); - $this->assertIsArray($response['body']['buildsFailure']); - $this->assertIsArray($response['body']['buildsSuccess']); + $this->assertIsArray($response['body']['buildsStorage']); $this->assertIsArray($response['body']['buildsTime']); + $this->assertIsArray($response['body']['executionsTotal']); + $this->assertIsArray($response['body']['executionsTime']); } /** diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index ff3f8e8e94..826c90cef8 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -445,7 +445,7 @@ class ProjectsConsoleClientTest extends Scope /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/usage', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/project/usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -454,14 +454,14 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(count($response['body']), 9); $this->assertNotEmpty($response['body']); $this->assertEquals('30d', $response['body']['range']); - $this->assertIsArray($response['body']['requests']); + $this->assertIsArray($response['body']['requestsTotal']); $this->assertIsArray($response['body']['network']); - $this->assertIsArray($response['body']['executions']); - $this->assertIsArray($response['body']['documents']); - $this->assertIsArray($response['body']['databases']); - $this->assertIsArray($response['body']['buckets']); - $this->assertIsArray($response['body']['users']); - $this->assertIsArray($response['body']['storage']); + $this->assertIsArray($response['body']['executionsTotal']); + $this->assertIsArray($response['body']['documentsTotal']); + $this->assertIsArray($response['body']['databasesTotal']); + $this->assertIsArray($response['body']['bucketsTotal']); + $this->assertIsArray($response['body']['usersTotal']); + $this->assertIsArray($response['body']['filesStorage']); /** * Test for FAILURE diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index 8fda8e0464..aff055f18d 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -39,10 +39,11 @@ class StorageConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(12, count($response['body'])); + $this->assertEquals(4, count($response['body'])); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['storage']); - $this->assertIsArray($response['body']['filesCount']); + $this->assertIsArray($response['body']['bucketsTotal']); + $this->assertIsArray($response['body']['filesTotal']); + $this->assertIsArray($response['body']['filesStorage']); } public function testGetStorageBucketUsage() @@ -94,13 +95,9 @@ class StorageConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 7); + $this->assertEquals(count($response['body']), 3); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['filesCount']); - $this->assertIsArray($response['body']['filesCreate']); - $this->assertIsArray($response['body']['filesRead']); - $this->assertIsArray($response['body']['filesUpdate']); - $this->assertIsArray($response['body']['filesDelete']); + $this->assertIsArray($response['body']['filesTotal']); $this->assertIsArray($response['body']['filesStorage']); } } From a643a8c9071462ccc11abe330c844593f30533c7 Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 4 Oct 2023 13:43:21 +0300 Subject: [PATCH 058/144] sync with 1.4 --- composer.json | 2 +- composer.lock | 18 +++++++++--------- src/Appwrite/Platform/Workers/Messaging.php | 1 - 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index 243906e99a..f7e4e56e04 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "utopia-php/image": "0.5.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", - "utopia-php/messaging": "0.1.*", + "utopia-php/messaging": "0.2.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "dev-integrate-workers as 0.3.3", diff --git a/composer.lock b/composer.lock index 5c1e3c9a9b..fd02d80d1d 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": "441bddff081659dfec29b0d735a6217a", + "content-hash": "748f1a5e4fdf0a73ea26ddf0e7822f19", "packages": [ { "name": "adhocore/jwt", @@ -1797,16 +1797,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.1.1", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", "shasum": "" }, "require": { @@ -1815,8 +1815,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" }, "type": "library", "autoload": { @@ -1839,9 +1839,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" }, - "time": "2023-02-07T05:42:46+00:00" + "time": "2023-09-14T20:48:42+00:00" }, { "name": "utopia-php/migration", diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index f38631c19b..76b86e4f0c 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -92,7 +92,6 @@ class Messaging extends Action return; } - $message = new SMS( to: [$payload['recipient']], content: $payload['message'], From 948f36a02397b67db6919e6d72350ec7281facb4 Mon Sep 17 00:00:00 2001 From: shimon Date: Thu, 5 Oct 2023 16:15:39 +0300 Subject: [PATCH 059/144] sync with 1.4 --- .gitmodules | 2 +- app/controllers/api/databases.php | 8 ++-- app/controllers/api/functions.php | 12 +++--- composer.json | 2 +- composer.lock | 42 +++++++++++-------- docker-compose.yml | 1 + phpunit.xml | 2 +- src/Appwrite/Platform/Workers/Databases.php | 3 +- .../e2e/Services/Databases/DatabasesBase.php | 1 + .../Databases/DatabasesCustomClientTest.php | 1 - 10 files changed, 41 insertions(+), 33 deletions(-) diff --git a/.gitmodules b/.gitmodules index 09a9253edd..effe15f68a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = feat-usage-1.4 + branch = feat-usage-sh-1.4 diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 10fd5265f9..7686690565 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -17,11 +17,10 @@ use MaxMind\Db\Reader; use Utopia\App; use Utopia\Audit\Audit; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Database; -use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; +use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; @@ -74,7 +73,7 @@ use Utopia\Validator\WhiteList; * @throws StructureException * @throws \Utopia\Database\Exception * @throws Conflict - * @throws \Utopia\Exception + * @throws Exception */ function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document { @@ -1204,7 +1203,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->inject('queueForDatabase') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - + //var_dump($elements); // use length of longest string as attribute size $size = 0; foreach ($elements as $element) { @@ -2936,7 +2935,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $cursor = \reset($cursor); - if ($cursor) { $documentId = $cursor->getValue(); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 58ffb0ac90..4195878bd0 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1692,16 +1692,18 @@ App::post('/v1/functions/:functionId/executions') Console::error($th->getMessage()); } + $queueForUsage + ->addMetric(METRIC_EXECUTIONS, 1) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function + ; + if ($function->getAttribute('logging')) { /** @var Document $execution */ $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); } - $queueForUsage - ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function - ; - $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); diff --git a/composer.json b/composer.json index f7e4e56e04..b7b6f74fbf 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.43.*", + "utopia-php/database": "dev-index-validation-no-attibute as 0.43.1", "utopia-php/domains": "0.3.*", "utopia-php/dsn": "0.1.*", "utopia-php/framework": "0.31.0", diff --git a/composer.lock b/composer.lock index fd02d80d1d..c504eb90b3 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": "748f1a5e4fdf0a73ea26ddf0e7822f19", + "content-hash": "1afac6ad40bf9191a18fcbf28de1420c", "packages": [ { "name": "adhocore/jwt", @@ -1433,16 +1433,16 @@ }, { "name": "utopia-php/database", - "version": "0.43.4", + "version": "dev-index-validation-no-attibute", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c" + "reference": "50e716c1522a290c4b2dd45b01aa4a7fcb5481c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c", - "reference": "cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c", + "url": "https://api.github.com/repos/utopia-php/database/zipball/50e716c1522a290c4b2dd45b01aa4a7fcb5481c2", + "reference": "50e716c1522a290c4b2dd45b01aa4a7fcb5481c2", "shasum": "" }, "require": { @@ -1483,9 +1483,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.43.4" + "source": "https://github.com/utopia-php/database/tree/index-validation-no-attibute" }, - "time": "2023-09-28T09:00:05+00:00" + "time": "2023-10-04T16:53:45+00:00" }, { "name": "utopia-php/domains", @@ -2007,12 +2007,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "04e6f0f08da7382cd20a5a5791a64c92745efa36" + "reference": "f7fe90764f0bcf73eea6f968965b2d786822033b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/04e6f0f08da7382cd20a5a5791a64c92745efa36", - "reference": "04e6f0f08da7382cd20a5a5791a64c92745efa36", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/f7fe90764f0bcf73eea6f968965b2d786822033b", + "reference": "f7fe90764f0bcf73eea6f968965b2d786822033b", "shasum": "" }, "require": { @@ -2020,8 +2020,7 @@ "ext-redis": "*", "php": ">=8.0", "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.31.*", - "utopia-php/queue": "0.5.*" + "utopia-php/framework": "0.31.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2039,9 +2038,9 @@ ], "description": "Light and Fast Platform Library", "keywords": [ - "cache", "framework", "php", + "platform", "upf", "utopia" ], @@ -2049,7 +2048,7 @@ "issues": "https://github.com/utopia-php/platform/issues", "source": "https://github.com/utopia-php/platform/tree/integrate-workers" }, - "time": "2023-09-27T16:50:44+00:00" + "time": "2023-10-04T14:44:45+00:00" }, { "name": "utopia-php/pools", @@ -2161,12 +2160,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7" + "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", - "reference": "2a2eee58dace58e75142d9e2e26d2f5104d7f3f7", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/5f81095e0ec66b2902037181ba3a20f6f9541af7", + "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7", "shasum": "" }, "require": { @@ -2214,7 +2213,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" }, - "time": "2023-07-12T07:55:31+00:00" + "time": "2023-10-04T14:49:13+00:00" }, { "name": "utopia-php/registry", @@ -5276,6 +5275,12 @@ } ], "aliases": [ + { + "package": "utopia-php/database", + "version": "dev-index-validation-no-attibute", + "alias": "0.43.1", + "alias_normalized": "0.43.1.0" + }, { "package": "utopia-php/platform", "version": "dev-integrate-workers", @@ -5291,6 +5296,7 @@ ], "minimum-stability": "stable", "stability-flags": { + "utopia-php/database": 20, "utopia-php/platform": 20, "utopia-php/queue": 20 }, diff --git a/docker-compose.yml b/docker-compose.yml index 51608a444c..d3f8386060 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -366,6 +366,7 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src + - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database depends_on: - redis - mariadb diff --git a/phpunit.xml b/phpunit.xml index f83f9f0fae..3d15bbd21a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" + stopOnFailure="true" > diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 6ff3a5d48b..788924a238 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -348,7 +348,7 @@ class Databases extends Action } if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project); + $this->deleteIndex($database, $collection, $index, $project, $dbForConsole, $dbForProject); } else { $dbForProject->updateDocument('indexes', $index->getId(), $index); } @@ -470,6 +470,7 @@ class Databases extends Action } catch (\Exception $e) { Console::error($e->getMessage()); + if ($e instanceof DatabaseException) { $index->setAttribute('error', $e->getMessage()); } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 88a7f99314..f869782ab5 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1057,6 +1057,7 @@ trait DatabasesBase $this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']); $this->assertEquals($releaseWithDate['body']['key'], $movies['body']['indexes'][2]['key']); $this->assertEquals('available', $movies['body']['indexes'][0]['status']); + var_dump($movies['body']['indexes']); $this->assertEquals('available', $movies['body']['indexes'][1]['status']); $this->assertEquals('available', $movies['body']['indexes'][2]['status']); diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index 743df9e53a..053f886032 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -80,7 +80,6 @@ class DatabasesCustomClientTest extends Scope Permission::write(Role::user($this->getUser()['$id'])), ] ]); - $this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); From 24e09d3d8eba4e8a82561d5916ea227f05d880e6 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 9 Oct 2023 13:16:05 +0300 Subject: [PATCH 060/144] queueForBuilds fix via functions controller --- app/controllers/api/databases.php | 4 +--- app/controllers/api/functions.php | 15 ++++++++------- composer.json | 2 +- composer.lock | 16 ++++++++-------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 7686690565..ace53b2f2b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -36,8 +36,6 @@ use Utopia\Database\Validator\Index as IndexValidator; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\Queries; -use Utopia\Database\Validator\Queries\Document as DocumentQueriesValidator; -use Utopia\Database\Validator\Queries\Documents; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Structure; @@ -2455,7 +2453,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'orders' => $orders, ]); - $validator = new IndexValidator($dbForProject->getAdapter()->getMaxIndexLength()); + $validator = new IndexValidator($attributes, $dbForProject->getAdapter()->getMaxIndexLength()); if (!$validator->isValid($collection->setAttribute('indexes', $index, Document::SET_TYPE_APPEND))) { throw new Exception(Exception::INDEX_INVALID, $validator->getDescription()); } diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 4195878bd0..383adebe67 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -49,7 +49,7 @@ use Utopia\VCS\Adapter\Git\GitHub; include_once __DIR__ . '/../shared/api.php'; -$redeployVcs = function (Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Document $template, GitHub $github) { +$redeployVcs = function (Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github) { $deploymentId = ID::unique(); $entrypoint = $function->getAttribute('entrypoint', ''); $providerInstallationId = $installation->getAttribute('providerInstallationId', ''); @@ -108,8 +108,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project 'activate' => true, ])); - $buildEvent = new Build(); - $buildEvent + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) ->setDeployment($deployment) @@ -158,9 +157,10 @@ App::post('/v1/functions') ->inject('project') ->inject('user') ->inject('queueForEvents') + ->inject('queueForBuilds') ->inject('dbForConsole') ->inject('gitHub') - ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Database $dbForConsole, GitHub $github) use ($redeployVcs) { + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; // build from template @@ -260,7 +260,7 @@ App::post('/v1/functions') // Redeploy vcs logic if (!empty($providerRepositoryId)) { - $redeployVcs($request, $function, $project, $installation, $dbForProject, $template, $github); + $redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github); } $functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS', ''); @@ -633,9 +633,10 @@ App::put('/v1/functions/:functionId') ->inject('dbForProject') ->inject('project') ->inject('queueForEvents') + ->inject('queueForBuilds') ->inject('dbForConsole') ->inject('gitHub') - ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForConsole, GitHub $github) use ($redeployVcs) { + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) { // TODO: If only branch changes, re-deploy $function = $dbForProject->getDocument('functions', $functionId); @@ -760,7 +761,7 @@ App::put('/v1/functions/:functionId') // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $redeployVcs($request, $function, $project, $installation, $dbForProject, new Document(), $github); + $redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github); } // Inform scheduler if function is still active diff --git a/composer.json b/composer.json index b7b6f74fbf..cb86574d61 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-index-validation-no-attibute as 0.43.1", + "utopia-php/database": "dev-fix-index-validation as 0.43.1", "utopia-php/domains": "0.3.*", "utopia-php/dsn": "0.1.*", "utopia-php/framework": "0.31.0", diff --git a/composer.lock b/composer.lock index c504eb90b3..598394db9f 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": "1afac6ad40bf9191a18fcbf28de1420c", + "content-hash": "15224f89284384114e0eef2ae83f49ea", "packages": [ { "name": "adhocore/jwt", @@ -1433,16 +1433,16 @@ }, { "name": "utopia-php/database", - "version": "dev-index-validation-no-attibute", + "version": "dev-fix-index-validation", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "50e716c1522a290c4b2dd45b01aa4a7fcb5481c2" + "reference": "67adc586318c35b34aaa9f1eed8f3751b6e8c324" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/50e716c1522a290c4b2dd45b01aa4a7fcb5481c2", - "reference": "50e716c1522a290c4b2dd45b01aa4a7fcb5481c2", + "url": "https://api.github.com/repos/utopia-php/database/zipball/67adc586318c35b34aaa9f1eed8f3751b6e8c324", + "reference": "67adc586318c35b34aaa9f1eed8f3751b6e8c324", "shasum": "" }, "require": { @@ -1483,9 +1483,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/index-validation-no-attibute" + "source": "https://github.com/utopia-php/database/tree/fix-index-validation" }, - "time": "2023-10-04T16:53:45+00:00" + "time": "2023-10-09T02:11:28+00:00" }, { "name": "utopia-php/domains", @@ -5277,7 +5277,7 @@ "aliases": [ { "package": "utopia-php/database", - "version": "dev-index-validation-no-attibute", + "version": "dev-fix-index-validation", "alias": "0.43.1", "alias_normalized": "0.43.1.0" }, From e2989afda804d310c7b1a62ddb10b65a9bd7c219 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 9 Oct 2023 13:28:06 +0300 Subject: [PATCH 061/144] queueForBuilds fix via functions controller --- app/controllers/api/vcs.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index b0050c61d4..55602136fc 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -39,7 +39,7 @@ use Utopia\Validator\Boolean; use function Swoole\Coroutine\batch; -$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, callable $getProjectDB, Request $request) { +$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request) { foreach ($repositories as $resource) { $resourceType = $resource->getAttribute('resourceType'); @@ -213,8 +213,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name); } - $buildEvent = new Build(); - $buildEvent + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) ->setDeployment($deployment) @@ -792,8 +791,9 @@ App::post('/v1/vcs/github/events') ->inject('response') ->inject('dbForConsole') ->inject('getProjectDB') + ->inject('queueForBuilds') ->action( - function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB) use ($createGitDeployments) { + function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); $signatureLocal = App::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); @@ -834,7 +834,7 @@ App::post('/v1/vcs/github/events') // create new deployment only on push and not when branch is created if (!$providerBranchCreated) { - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request); } } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { @@ -890,7 +890,7 @@ App::post('/v1/vcs/github/events') Query::orderDesc('$createdAt') ]); - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request); } elseif ($parsedPayload["action"] == "closed") { // Allowed external contributions cleanup @@ -1054,7 +1054,8 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor ->inject('project') ->inject('dbForConsole') ->inject('getProjectDB') - ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB) use ($createGitDeployments) { + ->inject('queueForBuilds') + ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $installation = $dbForConsole->getDocument('installations', $installationId); if ($installation->isEmpty()) { @@ -1095,7 +1096,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; $providerCommitHash = $pullRequestResponse['head']['sha'] ?? ''; - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request); $response->noContent(); }); From 002fc19d608aeb7252f7b9070ebc06ec4b700397 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 10 Oct 2023 21:02:24 +0300 Subject: [PATCH 062/144] tests fix --- app/controllers/api/databases.php | 3 +- app/controllers/api/users.php | 4 +- app/worker.php | 4 +- composer.json | 2 +- composer.lock | 21 ++--- phpunit.xml | 2 +- .../e2e/Services/Databases/DatabasesBase.php | 1 - .../Databases/DatabasesConsoleClientTest.php | 9 +-- tests/e2e/Services/Teams/TeamsBaseServer.php | 2 +- .../Services/Users/UsersConsoleClientTest.php | 43 +--------- tests/unit/Event/EventTest.php | 47 ++++++----- tests/unit/Usage/StatsTest.php | 79 ++++++++----------- 12 files changed, 80 insertions(+), 137 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index ace53b2f2b..b4709e656e 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1201,7 +1201,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->inject('queueForDatabase') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - //var_dump($elements); // use length of longest string as attribute size $size = 0; foreach ($elements as $element) { @@ -2453,7 +2452,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'orders' => $orders, ]); - $validator = new IndexValidator($attributes, $dbForProject->getAdapter()->getMaxIndexLength()); + $validator = new IndexValidator($dbForProject->getAdapter()->getMaxIndexLength()); if (!$validator->isValid($collection->setAttribute('indexes', $index, Document::SET_TYPE_APPEND))) { throw new Exception(Exception::INDEX_INVALID, $validator->getDescription()); } diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 99322f5bad..bd709ee272 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -735,7 +735,7 @@ App::put('/v1/users/:userId/labels') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, array $labels, Response $response, Database $dbForProject, Event $queueForEvents) { $user = $dbForProject->getDocument('users', $userId); @@ -1222,7 +1222,7 @@ App::get('/v1/users/usage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USAGE_USERS) - ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') ->inject('register') diff --git a/app/worker.php b/app/worker.php index be4fd2c665..572d876d6f 100644 --- a/app/worker.php +++ b/app/worker.php @@ -69,7 +69,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, ->pop() ->getResource(); - $adapter = new Database($database, $cache); + $adapter = new Database($database, $cache); $adapter->setNamespace('_' . $project->getInternalId()); return $adapter; }, ['cache', 'register', 'message', 'dbForConsole']); @@ -224,7 +224,7 @@ if (isset($args[0])) { try { $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'workersNum' => 1, 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, ]); diff --git a/composer.json b/composer.json index cb86574d61..f7e4e56e04 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-fix-index-validation as 0.43.1", + "utopia-php/database": "0.43.*", "utopia-php/domains": "0.3.*", "utopia-php/dsn": "0.1.*", "utopia-php/framework": "0.31.0", diff --git a/composer.lock b/composer.lock index 598394db9f..981cc546a1 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": "15224f89284384114e0eef2ae83f49ea", + "content-hash": "748f1a5e4fdf0a73ea26ddf0e7822f19", "packages": [ { "name": "adhocore/jwt", @@ -1433,16 +1433,16 @@ }, { "name": "utopia-php/database", - "version": "dev-fix-index-validation", + "version": "0.43.5", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "67adc586318c35b34aaa9f1eed8f3751b6e8c324" + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/67adc586318c35b34aaa9f1eed8f3751b6e8c324", - "reference": "67adc586318c35b34aaa9f1eed8f3751b6e8c324", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", "shasum": "" }, "require": { @@ -1483,9 +1483,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/fix-index-validation" + "source": "https://github.com/utopia-php/database/tree/0.43.5" }, - "time": "2023-10-09T02:11:28+00:00" + "time": "2023-10-06T06:49:47+00:00" }, { "name": "utopia-php/domains", @@ -5275,12 +5275,6 @@ } ], "aliases": [ - { - "package": "utopia-php/database", - "version": "dev-fix-index-validation", - "alias": "0.43.1", - "alias_normalized": "0.43.1.0" - }, { "package": "utopia-php/platform", "version": "dev-integrate-workers", @@ -5296,7 +5290,6 @@ ], "minimum-stability": "stable", "stability-flags": { - "utopia-php/database": 20, "utopia-php/platform": 20, "utopia-php/queue": 20 }, diff --git a/phpunit.xml b/phpunit.xml index 3d15bbd21a..f83f9f0fae 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" > diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index f869782ab5..88a7f99314 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1057,7 +1057,6 @@ trait DatabasesBase $this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']); $this->assertEquals($releaseWithDate['body']['key'], $movies['body']['indexes'][2]['key']); $this->assertEquals('available', $movies['body']['indexes'][0]['status']); - var_dump($movies['body']['indexes']); $this->assertEquals('available', $movies['body']['indexes'][1]['status']); $this->assertEquals('available', $movies['body']['indexes'][2]['status']); diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index 1476c8b28a..f51da53cb1 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -296,7 +296,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'limit' => 1 + 'queries' => ['offset(1)', 'limit(1)'], ]); $this->assertEquals(200, $logs['headers']['status-code']); @@ -308,8 +308,8 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'offset' => 1 - ]); + 'queries' => ['offset(1)'] + ]); $this->assertEquals(200, $logs['headers']['status-code']); $this->assertIsArray($logs['body']['logs']); @@ -319,8 +319,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'offset' => 1, - 'limit' => 1 + 'queries' => ['offset(1)', 'limit(1)'], ]); $this->assertEquals(200, $logs['headers']['status-code']); diff --git a/tests/e2e/Services/Teams/TeamsBaseServer.php b/tests/e2e/Services/Teams/TeamsBaseServer.php index 7bb6241c4a..9db17abaae 100644 --- a/tests/e2e/Services/Teams/TeamsBaseServer.php +++ b/tests/e2e/Services/Teams/TeamsBaseServer.php @@ -234,7 +234,7 @@ trait TeamsBaseServer /** * @depends testUpdateMembershipRoles */ - public function testDeleteUserUpdatesTeamMembershipCount($data) + public function testDeleteUserUpdatesTeamMembershipCount($data): void { $teamUid = $data['teamUid'] ?? ''; $userUid = $data['userUid'] ?? ''; diff --git a/tests/e2e/Services/Users/UsersConsoleClientTest.php b/tests/e2e/Services/Users/UsersConsoleClientTest.php index 9cc23e825a..8943bfab63 100644 --- a/tests/e2e/Services/Users/UsersConsoleClientTest.php +++ b/tests/e2e/Services/Users/UsersConsoleClientTest.php @@ -23,17 +23,6 @@ class UsersConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ 'range' => '32h', - 'provider' => 'email' - ]); - - $this->assertEquals($response['headers']['status-code'], 400); - - $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h', - 'provider' => 'some-random-provider' ]); $this->assertEquals($response['headers']['status-code'], 400); @@ -46,38 +35,12 @@ class UsersConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ 'range' => '24h', - 'provider' => 'email' ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 9); + $this->assertEquals(count($response['body']), 3); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['usersCount']); - $this->assertIsArray($response['body']['usersCreate']); - $this->assertIsArray($response['body']['usersRead']); - $this->assertIsArray($response['body']['usersUpdate']); - $this->assertIsArray($response['body']['usersDelete']); - $this->assertIsArray($response['body']['sessionsCreate']); - $this->assertIsArray($response['body']['sessionsProviderCreate']); - $this->assertIsArray($response['body']['sessionsDelete']); - - $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h' - ]); - - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 9); - $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['usersCount']); - $this->assertIsArray($response['body']['usersCreate']); - $this->assertIsArray($response['body']['usersRead']); - $this->assertIsArray($response['body']['usersUpdate']); - $this->assertIsArray($response['body']['usersDelete']); - $this->assertIsArray($response['body']['sessionsCreate']); - $this->assertIsArray($response['body']['sessionsProviderCreate']); - $this->assertIsArray($response['body']['sessionsDelete']); + $this->assertIsArray($response['body']['usersTotal']); + $this->assertIsArray($response['body']['sessionsTotal']); } } diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index a328c8d599..b48fc8f849 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -3,67 +3,74 @@ namespace Tests\Unit\Event; use Appwrite\Event\Event; +use Appwrite\URL\URL; use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Utopia\App; +use Utopia\DSN\DSN; +use Utopia\Queue; +use Utopia\Queue\Client; + +require_once __DIR__ . '/../../../app/init.php'; class EventTest extends TestCase { + protected ?Event $object = null; protected string $queue = ''; public function setUp(): void { - $redisHost = App::getEnv('_APP_REDIS_HOST', ''); - $redisPort = App::getEnv('_APP_REDIS_PORT', ''); - \Resque::setBackend($redisHost . ':' . $redisPort); + $fallbackForRedis = URL::unparse([ + 'scheme' => 'redis', + 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => App::getEnv('_APP_REDIS_USER', ''), + 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + ]); + $dsn = App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis); + $dsn = explode('=', $dsn); + $dsn = $dsn[0] ?? ''; + $dsn = new DSN($dsn); + $connection = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); $this->queue = 'v1-tests' . uniqid(); - $this->object = new Event($this->queue, 'TestsV1'); + $this->object = new Event($connection); + $this->object->setClass('TestsV1'); + $this->object->setQueue($this->queue); } public function testQueue(): void { $this->assertEquals($this->queue, $this->object->getQueue()); - $this->object->setQueue('demo'); - $this->assertEquals('demo', $this->object->getQueue()); - $this->object->setQueue($this->queue); } public function testClass(): void { $this->assertEquals('TestsV1', $this->object->getClass()); - $this->object->setClass('TestsV2'); - $this->assertEquals('TestsV2', $this->object->getClass()); - $this->object->setClass('TestsV1'); } public function testParams(): void { + $this->object ->setParam('eventKey1', 'eventValue1') ->setParam('eventKey2', 'eventValue2'); $this->object->trigger(); - $this->assertEquals('eventValue1', $this->object->getParam('eventKey1')); $this->assertEquals('eventValue2', $this->object->getParam('eventKey2')); $this->assertEquals(null, $this->object->getParam('eventKey3')); - $this->assertEquals(\Resque::size($this->queue), 1); - } - - public function testPause(): void - { - $this->object->setPaused(true); - $this->assertTrue($this->object->isPaused()); - $this->object->setPaused(false); - $this->assertNotTrue($this->object->isPaused()); + global $register; + $pools = $register->get('pools'); + $client = new Client($this->object->getQueue(), $pools->get('queue')->pop()->getResource()); + $this->assertEquals($client->getQueueSize(), 1); } public function testReset(): void diff --git a/tests/unit/Usage/StatsTest.php b/tests/unit/Usage/StatsTest.php index f021841396..794caebdf5 100644 --- a/tests/unit/Usage/StatsTest.php +++ b/tests/unit/Usage/StatsTest.php @@ -2,70 +2,53 @@ namespace Tests\Unit\Usage; -use Appwrite\Usage\Stats; +use Appwrite\URL\URL as AppwriteURL; use PHPUnit\Framework\TestCase; use Utopia\App; +use Utopia\DSN\DSN; +use Utopia\Queue; +use Utopia\Queue\Client; +use Utopia\Queue\Connection; class StatsTest extends TestCase { - /** - * @var Stats - */ - protected $object = null; + protected ?Connection $connection = null; + protected ?Client $client = null; + + protected const QUEUE_NAME = 'usage-test-q'; public function setUp(): void { - $host = App::getEnv('_APP_STATSD_HOST', 'telegraf'); - $port = App::getEnv('_APP_STATSD_PORT', 8125); + $env = App::getEnv('_APP_CONNECTIONS_QUEUE', AppwriteURL::unparse([ + 'scheme' => 'redis', + 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => App::getEnv('_APP_REDIS_USER', ''), + 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + ])); - $connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port); - $statsd = new \Domnikl\Statsd\Client($connection); - - $this->object = new Stats($statsd); + $dsn = explode('=', $env); + $dsn = count($dsn) > 1 ? $dsn[1] : $dsn[0]; + $dsn = new DSN($dsn); + $this->connection = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); + $this->client = new Client(self::QUEUE_NAME, $this->connection); } public function tearDown(): void { } - public function testNamespace(): void + public function testSamePayload(): void { - $this->object->setNamespace('appwritetest.usage'); - $this->assertEquals('appwritetest.usage', $this->object->getNamespace()); - } + $inToQueue = [ + 'key_1' => 'value_1', + 'key_2' => 'value_2', + ]; - public function testParams(): void - { - $this->object - ->setParam('projectId', 'appwrite_test') - ->setParam('projectInternalId', 1) - ->setParam('networkRequestSize', 100) - ; - - $this->assertEquals('appwrite_test', $this->object->getParam('projectId')); - $this->assertEquals(1, $this->object->getParam('projectInternalId')); - $this->assertEquals(100, $this->object->getParam('networkRequestSize')); - - $this->object->submit(); - - $this->assertEquals(null, $this->object->getParam('projectId')); - $this->assertEquals(null, $this->object->getParam('networkRequestSize')); - } - - public function testReset(): void - { - $this->object - ->setParam('projectId', 'appwrite_test') - ->setParam('networkRequestSize', 100) - ; - - $this->assertEquals('appwrite_test', $this->object->getParam('projectId')); - $this->assertEquals(100, $this->object->getParam('networkRequestSize')); - - $this->object->reset(); - - $this->assertEquals(null, $this->object->getParam('projectId')); - $this->assertEquals(null, $this->object->getParam('networkRequestSize')); - $this->assertEquals('appwrite.usage', $this->object->getNamespace()); + $result = $this->client->enqueue($inToQueue); + $this->assertTrue($result); + $outFromQueue = $this->connection->leftPopArray('utopia-queue.queue.' . self::QUEUE_NAME, 0)['payload']; + $this->assertNotEmpty($outFromQueue); + $this->assertSame($inToQueue, $outFromQueue); } } From 747a16c4badaad2b20aed26abbf2fbea0ce8616d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 16:50:21 +1300 Subject: [PATCH 063/144] Add env var for db queues --- .env | 1 + docker-compose.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.env b/.env index feeb74d849..bc58e43b6e 100644 --- a/.env +++ b/.env @@ -4,6 +4,7 @@ _APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= +_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1 _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/docker-compose.yml b/docker-compose.yml index b229609d5b..76e9665aba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -366,6 +366,7 @@ services: - mariadb environment: - _APP_ENV + - _APP_CONNECTION_DB_QUEUES - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From 2cf0ed1d69fbb9a9d65cc31e958d2c5459e08e27 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 16:51:43 +1300 Subject: [PATCH 064/144] Remove static queue set for database worker --- bin/worker-databases | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/worker-databases b/bin/worker-databases index bbec58268f..4f46e8f40b 100644 --- a/bin/worker-databases +++ b/bin/worker-databases @@ -7,4 +7,4 @@ else REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" fi -INTERVAL=0.1 QUEUE='v1-database' APP_INCLUDE='/usr/src/code/app/workers/databases.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php \ No newline at end of file +INTERVAL=0.1 APP_INCLUDE='/usr/src/code/app/workers/databases.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php diff --git a/docker-compose.yml b/docker-compose.yml index 76e9665aba..b41237f5d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -366,7 +366,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTION_DB_QUEUES + - _APP_CONNECTIONS_DB_QUEUES - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From ca5b7f5b163f5f0efcf44fd33405e428db1cc57d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 16:52:58 +1300 Subject: [PATCH 065/144] Add dynamic queue setting for database worker --- app/workers/databases.php | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/app/workers/databases.php b/app/workers/databases.php index 764f668f33..3342aeb6c4 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -4,6 +4,7 @@ use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Resque\Worker; +use Utopia\App; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -14,10 +15,43 @@ require_once __DIR__ . '/../init.php'; Console::title('Database V1 Worker'); Console::success(APP_NAME . ' database worker v1 has started' . "\n"); +$table = new Swoole\Table(1); +$table->column('workerCount', Swoole\Table::TYPE_INT); +$table->create(); +$table->set('databases', ['workerCount' => 0]); + +$lock = new Swoole\Lock(SWOOLE_MUTEX); + class DatabaseV1 extends Worker { public function init(): void { + global $table, $lock; + + $dbQueues = App::getEnv('_APP_CONNECTIONS_DB_QUEUES'); + + if (empty($dbQueues)) { + $queue = 'v1-database'; + } elseif (\str_contains($dbQueues, ',')) { + $dbQueues = \explode(',', $dbQueues); + $dbQueues = \array_map('trim', $dbQueues); + $dbQueues = \array_filter($dbQueues); + $dbQueues = \array_values($dbQueues); + + $count = $table->get('databases', 'workerCount'); + + Console::log('Database worker count: ' . $count); + + $queue = $dbQueues[$count]; + } else { + $queue = \trim($dbQueues); + } + + \putenv('QUEUE=' . $queue); + + $lock->lock(); + $table->incr('databases', 'workerCount'); + $lock->unlock(); } public function run(): void @@ -58,6 +92,11 @@ class DatabaseV1 extends Worker public function shutdown(): void { + global $table, $lock; + + $lock->lock(); + $table->decr('databases', 'workerCount'); + $lock->unlock(); } /** From 365630ff39ab98ff218fdf8d92bad4ef09012da0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 17:21:40 +1300 Subject: [PATCH 066/144] Set the queue name based on project DB --- .env | 2 +- app/workers/databases.php | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/.env b/.env index bc58e43b6e..b3a57baae5 100644 --- a/.env +++ b/.env @@ -4,7 +4,7 @@ _APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= -_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1 +_APP_CONNECTIONS_QUEUE_PER_WORKER=enabled _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/app/workers/databases.php b/app/workers/databases.php index 3342aeb6c4..46a7074623 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -28,23 +28,13 @@ class DatabaseV1 extends Worker { global $table, $lock; - $dbQueues = App::getEnv('_APP_CONNECTIONS_DB_QUEUES'); + $dbQueues = App::getEnv('_APP_CONNECTIONS_QUEUE_PER_WORKER', 'disabled'); - if (empty($dbQueues)) { + if ($dbQueues !== 'enabled') { $queue = 'v1-database'; - } elseif (\str_contains($dbQueues, ',')) { - $dbQueues = \explode(',', $dbQueues); - $dbQueues = \array_map('trim', $dbQueues); - $dbQueues = \array_filter($dbQueues); - $dbQueues = \array_values($dbQueues); - - $count = $table->get('databases', 'workerCount'); - - Console::log('Database worker count: ' . $count); - - $queue = $dbQueues[$count]; } else { - $queue = \trim($dbQueues); + $project = new Document($this->args['project']); + $queue = $project->getAttribute('database'); } \putenv('QUEUE=' . $queue); From d238d79e2924590f7a638c4112dd1d64131f137c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 21:28:29 +1300 Subject: [PATCH 067/144] Allow multiprocess for db queue --- .env | 1 + app/workers/databases.php | 28 ---------------------------- bin/worker-databases | 16 +++++++++++++++- docker-compose.yml | 1 + src/Appwrite/Event/Database.php | 11 ++++++++++- 5 files changed, 27 insertions(+), 30 deletions(-) diff --git a/.env b/.env index b3a57baae5..4777d7fdcb 100644 --- a/.env +++ b/.env @@ -5,6 +5,7 @@ _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= _APP_CONNECTIONS_QUEUE_PER_WORKER=enabled +_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1,v1-database-2 _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/app/workers/databases.php b/app/workers/databases.php index 46a7074623..f0c90ccfe4 100644 --- a/app/workers/databases.php +++ b/app/workers/databases.php @@ -15,33 +15,10 @@ require_once __DIR__ . '/../init.php'; Console::title('Database V1 Worker'); Console::success(APP_NAME . ' database worker v1 has started' . "\n"); -$table = new Swoole\Table(1); -$table->column('workerCount', Swoole\Table::TYPE_INT); -$table->create(); -$table->set('databases', ['workerCount' => 0]); - -$lock = new Swoole\Lock(SWOOLE_MUTEX); - class DatabaseV1 extends Worker { public function init(): void { - global $table, $lock; - - $dbQueues = App::getEnv('_APP_CONNECTIONS_QUEUE_PER_WORKER', 'disabled'); - - if ($dbQueues !== 'enabled') { - $queue = 'v1-database'; - } else { - $project = new Document($this->args['project']); - $queue = $project->getAttribute('database'); - } - - \putenv('QUEUE=' . $queue); - - $lock->lock(); - $table->incr('databases', 'workerCount'); - $lock->unlock(); } public function run(): void @@ -82,11 +59,6 @@ class DatabaseV1 extends Worker public function shutdown(): void { - global $table, $lock; - - $lock->lock(); - $table->decr('databases', 'workerCount'); - $lock->unlock(); } /** diff --git a/bin/worker-databases b/bin/worker-databases index 4f46e8f40b..6f27d3d13e 100644 --- a/bin/worker-databases +++ b/bin/worker-databases @@ -7,4 +7,18 @@ else REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" fi -INTERVAL=0.1 APP_INCLUDE='/usr/src/code/app/workers/databases.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php +queues="${_APP_CONNECTIONS_DB_QUEUES}" +if [ -z "${queues}" ]; then + queues="v1-databases" +fi + +count=1 +if [ "${_APP_CONNECTIONS_QUEUE_PER_WORKER}" = "enabled" ]; then + count=$(echo "${queues}" | tr ',' '\n' | wc -l) +fi + +INTERVAL=0.1 \ + QUEUE="${queues}" \ + COUNT=${count} \ + APP_INCLUDE='/usr/src/code/app/workers/databases.php' \ + php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php diff --git a/docker-compose.yml b/docker-compose.yml index b41237f5d8..e231f4d7d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -366,6 +366,7 @@ services: - mariadb environment: - _APP_ENV + - _APP_CONNECTIONS_QUEUE_PER_WORKER - _APP_CONNECTIONS_DB_QUEUES - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 1822f06c71..f36688eb01 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -3,6 +3,7 @@ namespace Appwrite\Event; use Resque; +use Utopia\App; use Utopia\Database\Document; class Database extends Event @@ -14,7 +15,15 @@ class Database extends Event public function __construct() { - parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME); + $dbQueues = App::getEnv('_APP_CONNECTIONS_DB_QUEUES'); + + if (empty($dbQueues)) { + $queue = Event::DATABASE_QUEUE_NAME; + } else { + $queue = $this->getProject()->getAttribute('database'); + } + + parent::__construct($queue, Event::DATABASE_CLASS_NAME); } /** From 92adaee1cb8b3268fee8549cd2bf35fea6546b07 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 11 Oct 2023 14:57:11 +0200 Subject: [PATCH 068/144] chore: upgrade console to 3.2.2 on cloud --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8ec131892e..5506e01d6f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.1 + branch = 3.2.2 diff --git a/app/console b/app/console index 2f47e4e77b..0f9e5e7839 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 2f47e4e77b3c832679c5a83a604dd7b8e8fc1903 +Subproject commit 0f9e5e78396dcfe8b2a02131afe874bd84ba4085 From 2cd451408607651a2c5a9105a875fd95def3d623 Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 11 Oct 2023 16:03:15 +0300 Subject: [PATCH 069/144] sync with main --- app/worker.php | 2 +- composer.json | 4 +- .../Utopia/Response/Model/Migration.php | 1 - src/Executor/Executor.php | 13 +- tests/e2e/Services/Avatars/AvatarsBase.php | 2 +- .../Functions/FunctionsCustomServerTest.php | 125 +------------- tests/e2e/Services/GraphQL/AvatarsTest.php | 2 +- .../Health/HealthCustomServerTest.php | 157 +++++------------- tests/e2e/Services/Users/UsersBase.php | 12 -- tests/resources/docker/docker-compose.yml | 2 - tests/resources/functions/php-event/index.php | 8 - 11 files changed, 56 insertions(+), 272 deletions(-) delete mode 100644 tests/resources/functions/php-event/index.php diff --git a/app/worker.php b/app/worker.php index 572d876d6f..7f8f138af7 100644 --- a/app/worker.php +++ b/app/worker.php @@ -224,7 +224,7 @@ if (isset($args[0])) { try { $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => 1, + 'workersNum' => strtolower($workerName) === 'databases'? 1 :swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, ]); diff --git a/composer.json b/composer.json index f7e4e56e04..b5d2392406 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "utopia-php/image": "0.5.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", - "utopia-php/messaging": "0.2.*", + "utopia-php/messaging": "0.1.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "dev-integrate-workers as 0.3.3", @@ -64,7 +64,7 @@ "utopia-php/preloader": "0.2.*", "utopia-php/queue": "dev-feat-get-worker-start as 0.5.3", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.17.*", + "utopia-php/storage": "0.14.*", "utopia-php/swoole": "0.5.*", "utopia-php/vcs": "0.5.*", "utopia-php/websocket": "0.1.*", diff --git a/src/Appwrite/Utopia/Response/Model/Migration.php b/src/Appwrite/Utopia/Response/Model/Migration.php index 5a54eef3ad..78e8658032 100644 --- a/src/Appwrite/Utopia/Response/Model/Migration.php +++ b/src/Appwrite/Utopia/Response/Model/Migration.php @@ -68,7 +68,6 @@ class Migration extends Model ->addRule('errors', [ 'type' => self::TYPE_STRING, 'description' => 'All errors that occurred during the migration process.', - 'array' => true, 'default' => [], 'example' => [], ]) diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index c7388069a1..36f6ad0dc5 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -70,7 +70,7 @@ class Executor array $variables = [], string $command = null, ) { - $runtimeId = "$projectId-$deploymentId-build"; + $runtimeId = "$projectId-$deploymentId"; $route = "/runtimes"; $params = [ 'runtimeId' => $runtimeId, @@ -113,7 +113,7 @@ class Executor ) { $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); - $runtimeId = "$projectId-$deploymentId-build"; + $runtimeId = "$projectId-$deploymentId"; $route = "/runtimes/{$runtimeId}/logs"; $params = [ 'timeout' => $timeout @@ -177,7 +177,6 @@ class Executor string $method, array $headers, string $runtimeEntrypoint = null, - int $requestTimeout = null ) { if (empty($headers['host'])) { $headers['host'] = App::getEnv('_APP_DOMAIN', ''); @@ -203,13 +202,9 @@ class Executor 'runtimeEntrypoint' => $runtimeEntrypoint, ]; - // Safety timeout. Executor has timeout, and open runtime has soft timeout. - // This one shouldn't really happen, but prevents from unexpected networking behaviours. - if ($requestTimeout == null) { - $requestTimeout = $timeout + 15; - } + $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); - $response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $requestTimeout); + $response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); $status = $response['headers']['status-code']; if ($status >= 400) { diff --git a/tests/e2e/Services/Avatars/AvatarsBase.php b/tests/e2e/Services/Avatars/AvatarsBase.php index ba66920ed6..b8581b952e 100644 --- a/tests/e2e/Services/Avatars/AvatarsBase.php +++ b/tests/e2e/Services/Avatars/AvatarsBase.php @@ -202,7 +202,7 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/image', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/images/open-graph/website.png', + 'url' => 'https://appwrite.io/images/apple.png', ]); $this->assertEquals(200, $response['headers']['status-code']); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index cdc9ec846f..60b7f7542e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -32,8 +32,8 @@ class FunctionsCustomServerTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [ - 'buckets.*.create', - 'buckets.*.delete', + 'users.*.create', + 'users.*.delete', ], 'schedule' => '0 0 1 1 *', 'timeout' => 10, @@ -50,8 +50,8 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, $dateValidator->isValid($response1['body']['$updatedAt'])); $this->assertEquals('', $response1['body']['deployment']); $this->assertEquals([ - 'buckets.*.create', - 'buckets.*.delete', + 'users.*.create', + 'users.*.delete', ], $response1['body']['events']); $this->assertEquals('0 0 1 1 *', $response1['body']['schedule']); $this->assertEquals(10, $response1['body']['timeout']); @@ -191,8 +191,8 @@ class FunctionsCustomServerTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [ - 'buckets.*.create', - 'buckets.*.delete', + 'users.*.create', + 'users.*.delete', ], 'schedule' => '0 0 1 1 *', 'timeout' => 10, @@ -1231,117 +1231,4 @@ class FunctionsCustomServerTest extends Scope $this->assertArrayHasKey('base', $runtime); $this->assertArrayHasKey('supports', $runtime); } - - - public function testEventTrigger() - { - $timeout = 5; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-event/code.tar.gz"; - $this->packageCode('php-event'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'functionId' => ID::unique(), - 'name' => 'Test PHP Event executions', - 'runtime' => 'php-8.0', - 'entrypoint' => 'index.php', - 'events' => [ - 'users.*.create', - ], - 'timeout' => $timeout, - ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), - 'activate' => true - ]); - - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - // Poll until deployment is built - while (true) { - $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - - if ( - $deployment['headers']['status-code'] >= 400 - || \in_array($deployment['body']['status'], ['ready', 'failed']) - ) { - break; - } - - \sleep(1); - } - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); - - // Create user to trigger event - $user = $this->client->call(Client::METHOD_POST, '/users', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'userId' => 'unique()', - 'name' => 'Event User' - ]); - - $userId = $user['body']['$id']; - - $this->assertEquals(201, $user['headers']['status-code']); - - // Wait for execution to occur - sleep(15); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $execution = $executions['body']['executions'][0]; - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertEquals('completed', $execution['status']); - $this->assertEquals(204, $execution['responseStatusCode']); - $this->assertStringContainsString($userId, $execution['logs']); - $this->assertStringContainsString('Event User', $execution['logs']); - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - - // Cleanup : Delete user - $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - } } diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index b95e3b251f..cfb9061df3 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -155,7 +155,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(5041, \strlen($initials['body'])); + $this->assertEquals(4959, \strlen($initials['body'])); return $initials['body']; } diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 4ac15bd95c..96c9bde5c7 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -29,6 +29,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['ping']); $this->assertLessThan(100, $response['body']['ping']); + /** + * Test for FAILURE + */ + return []; } @@ -47,6 +51,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); + /** + * Test for FAILURE + */ + return []; } @@ -65,6 +73,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); + /** + * Test for FAILURE + */ + return []; } @@ -83,6 +95,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); + /** + * Test for FAILURE + */ + return []; } @@ -101,6 +117,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); + /** + * Test for FAILURE + */ + return []; } @@ -121,6 +141,10 @@ class HealthCustomServerTest extends Scope $this->assertNotEmpty($response['body']['localTime']); $this->assertLessThan(10, $response['body']['diff']); + /** + * Test for FAILURE + */ + return []; } @@ -138,6 +162,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); + /** + * Test for FAILURE + */ + return []; } @@ -155,6 +183,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); + /** + * Test for FAILURE + */ + return []; } @@ -172,124 +204,9 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); - return []; - } - - public function testFunctionsSuccess(): array - { /** - * Test for SUCCESS + * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - return []; - } - - public function testBuildsSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/builds', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - return []; - } - - public function testDatabasesSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/databases', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - return []; - } - - public function testDeletesSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/deletes', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - return []; - } - - public function testMailsSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/mails', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - return []; - } - - public function testMessagingSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/messaging', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); - - return []; - } - - public function testMigrationsSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/migrations', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(100, $response['body']['size']); return []; } @@ -309,6 +226,10 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['ping']); $this->assertLessThan(100, $response['body']['ping']); + /** + * Test for FAILURE + */ + return []; } @@ -327,6 +248,10 @@ class HealthCustomServerTest extends Scope $this->assertIsString($response['body']['status']); $this->assertIsString($response['body']['version']); + /** + * Test for FAILURE + */ + return []; } } diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 3327bb7558..baf601789a 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -595,18 +595,6 @@ trait UsersBase $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); - $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'search' => '>', - ]); - - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertNotEmpty($response['body']); - $this->assertEmpty($response['body']['users']); - $this->assertCount(0, $response['body']['users']); - /** * Test for FAILURE */ diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index bdb9bf49a4..3baae7316d 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -66,9 +66,7 @@ services: environment: - _APP_ENV - _APP_OPTIONS_ABUSE - - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS - - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_FUNCTIONS diff --git a/tests/resources/functions/php-event/index.php b/tests/resources/functions/php-event/index.php deleted file mode 100644 index 550fd57729..0000000000 --- a/tests/resources/functions/php-event/index.php +++ /dev/null @@ -1,8 +0,0 @@ -log($context->req->body['$id']); - $context->log($context->req->body['name']); - - return $context->res->empty(); -}; From 5d845208d9494930c3471ef106bdbe1e613a54d6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 15:02:53 +1300 Subject: [PATCH 070/144] Simplify --- .env | 3 +-- bin/worker-databases | 16 +--------------- docker-compose.yml | 3 +-- src/Appwrite/Event/Database.php | 13 ++++--------- 4 files changed, 7 insertions(+), 28 deletions(-) diff --git a/.env b/.env index 4777d7fdcb..0e5da80955 100644 --- a/.env +++ b/.env @@ -4,8 +4,7 @@ _APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= -_APP_CONNECTIONS_QUEUE_PER_WORKER=enabled -_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1,v1-database-2 +_APP_CONNECTIONS_DB_QUEUE=database-db-main _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/bin/worker-databases b/bin/worker-databases index 6f27d3d13e..364ffaa90b 100644 --- a/bin/worker-databases +++ b/bin/worker-databases @@ -7,18 +7,4 @@ else REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}" fi -queues="${_APP_CONNECTIONS_DB_QUEUES}" -if [ -z "${queues}" ]; then - queues="v1-databases" -fi - -count=1 -if [ "${_APP_CONNECTIONS_QUEUE_PER_WORKER}" = "enabled" ]; then - count=$(echo "${queues}" | tr ',' '\n' | wc -l) -fi - -INTERVAL=0.1 \ - QUEUE="${queues}" \ - COUNT=${count} \ - APP_INCLUDE='/usr/src/code/app/workers/databases.php' \ - php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php +INTERVAL=0.1 QUEUE="${_APP_CONNECTIONS_DB_QUEUE}" APP_INCLUDE="/usr/src/code/app/workers/databases.php" php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php diff --git a/docker-compose.yml b/docker-compose.yml index e231f4d7d2..8e7c392ac5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -366,8 +366,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTIONS_QUEUE_PER_WORKER - - _APP_CONNECTIONS_DB_QUEUES + - _APP_CONNECTIONS_DB_QUEUE - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index f36688eb01..81e032eacd 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -15,15 +15,10 @@ class Database extends Event public function __construct() { - $dbQueues = App::getEnv('_APP_CONNECTIONS_DB_QUEUES'); - - if (empty($dbQueues)) { - $queue = Event::DATABASE_QUEUE_NAME; - } else { - $queue = $this->getProject()->getAttribute('database'); - } - - parent::__construct($queue, Event::DATABASE_CLASS_NAME); + parent::__construct( + $this->getProject()->getAttribute('database'), + Event::DATABASE_CLASS_NAME + ); } /** From d7b87d7bae2d46e5e912c7e834e0bc395f7dfba0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 16:50:21 +1300 Subject: [PATCH 071/144] Add env var for db queues --- .env | 1 + docker-compose.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.env b/.env index feeb74d849..bc58e43b6e 100644 --- a/.env +++ b/.env @@ -4,6 +4,7 @@ _APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= +_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1 _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/docker-compose.yml b/docker-compose.yml index d7fe9a5af4..7a77b91ef5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,6 +372,7 @@ services: - mariadb environment: - _APP_ENV + - _APP_CONNECTION_DB_QUEUES - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From c0c6197cffeb43bc5e597c4e1af0b1396ab7bde7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 16:51:43 +1300 Subject: [PATCH 072/144] Remove static queue set for database worker --- bin/worker-databases | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/worker-databases b/bin/worker-databases index 502075bc58..61e09aa9f1 100644 --- a/bin/worker-databases +++ b/bin/worker-databases @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php databases $@ \ No newline at end of file +php /usr/src/code/app/worker.php databases $@ diff --git a/docker-compose.yml b/docker-compose.yml index 7a77b91ef5..56e35e5abf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,7 +372,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTION_DB_QUEUES + - _APP_CONNECTIONS_DB_QUEUES - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From 662233b026985469c0eea9f774882182a035b97c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 17:21:40 +1300 Subject: [PATCH 073/144] Set the queue name based on project DB --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index bc58e43b6e..b3a57baae5 100644 --- a/.env +++ b/.env @@ -4,7 +4,7 @@ _APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= -_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1 +_APP_CONNECTIONS_QUEUE_PER_WORKER=enabled _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io From 7a78f49eb98d95541b5c38f4d716a45220816017 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 11 Oct 2023 21:28:29 +1300 Subject: [PATCH 074/144] Allow multiprocess for db queue --- .env | 1 + docker-compose.yml | 1 + src/Appwrite/Event/Database.php | 12 +++++++++--- src/Appwrite/Platform/Workers/Databases.php | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.env b/.env index b3a57baae5..4777d7fdcb 100644 --- a/.env +++ b/.env @@ -5,6 +5,7 @@ _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= _APP_CONNECTIONS_QUEUE_PER_WORKER=enabled +_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1,v1-database-2 _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/docker-compose.yml b/docker-compose.yml index 56e35e5abf..92845376b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,6 +372,7 @@ services: - mariadb environment: - _APP_ENV + - _APP_CONNECTIONS_QUEUE_PER_WORKER - _APP_CONNECTIONS_DB_QUEUES - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 0741255664..c39246b3de 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -2,6 +2,7 @@ namespace Appwrite\Event; +use Utopia\App; use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -16,10 +17,15 @@ class Database extends Event public function __construct(protected Connection $connection) { parent::__construct($connection); + $dbQueues = App::getEnv('_APP_CONNECTIONS_DB_QUEUES'); - $this - ->setQueue(Event::DATABASE_QUEUE_NAME) - ->setClass(Event::DATABASE_CLASS_NAME); + if (empty($dbQueues)) { + $queue = Event::DATABASE_QUEUE_NAME; + } else { + $queue = $this->getProject()->getAttribute('database'); + } + + parent::__construct($queue, Event::DATABASE_CLASS_NAME); } /** diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 788924a238..d4881c0f71 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -481,7 +481,7 @@ class Databases extends Action ); } finally { $target = Realtime::fromPayload( - // Pass first, most verbose event pattern + // Pass first, most verbose event pattern event: $events[0], payload: $index, project: $project From 69abd222e8145cfd1f31300c507f26eeab6a3212 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 15:02:53 +1300 Subject: [PATCH 075/144] Simplify --- .env | 3 +-- docker-compose.yml | 3 +-- src/Appwrite/Event/Database.php | 11 +++-------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.env b/.env index 4777d7fdcb..0e5da80955 100644 --- a/.env +++ b/.env @@ -4,8 +4,7 @@ _APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= -_APP_CONNECTIONS_QUEUE_PER_WORKER=enabled -_APP_CONNECTIONS_DB_QUEUES=v1-database-0,v1-database-1,v1-database-2 +_APP_CONNECTIONS_DB_QUEUE=database-db-main _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/docker-compose.yml b/docker-compose.yml index 92845376b4..fe41866ac3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,8 +372,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTIONS_QUEUE_PER_WORKER - - _APP_CONNECTIONS_DB_QUEUES + - _APP_CONNECTIONS_DB_QUEUE - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index c39246b3de..8670346c45 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -17,15 +17,10 @@ class Database extends Event public function __construct(protected Connection $connection) { parent::__construct($connection); - $dbQueues = App::getEnv('_APP_CONNECTIONS_DB_QUEUES'); - if (empty($dbQueues)) { - $queue = Event::DATABASE_QUEUE_NAME; - } else { - $queue = $this->getProject()->getAttribute('database'); - } - - parent::__construct($queue, Event::DATABASE_CLASS_NAME); + $this + ->setQueue($this->getProject()->getAttribute('database')) + ->setClass(Event::DATABASE_CLASS_NAME); } /** From 096a52961882328b44f3824b9d15d734680f0f76 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 15:30:01 +1300 Subject: [PATCH 076/144] Revert "Merge branch 'feat-db-per-process' of github.com:appwrite/appwrite into feat-db-per-process" This reverts commit 6f42305484128061275760b2a45e6bedac50736f, reversing changes made to 69abd222e8145cfd1f31300c507f26eeab6a3212. --- app/controllers/api/account.php | 2 +- app/controllers/api/functions.php | 2 ++ app/controllers/api/teams.php | 2 +- app/controllers/api/users.php | 2 +- app/worker.php | 2 +- src/Appwrite/Event/Audit.php | 1 + tests/unit/Event/EventTest.php | 1 + 7 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index d6aaaa24bb..93df84a6f6 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1110,7 +1110,7 @@ App::put('/v1/account/sessions/magic-url') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + ->action(function (string $userId, string $secret, Request $request, Response $response,Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { /** @var Utopia\Database\Document $user */ diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 3a1c96690e..383adebe67 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1680,6 +1680,8 @@ App::post('/v1/functions/:functionId/executions') $execution->setAttribute('logs', $executionResponse['logs']); $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); + + } catch (\Throwable $th) { $durationEnd = \microtime(true); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 4a0378105f..99fcfeb38c 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -257,7 +257,7 @@ App::put('/v1/teams/:teamId') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp , Response $response, Database $dbForProject, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index a992d51965..bd709ee272 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -130,7 +130,7 @@ App::post('/v1/users') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response,Document $project, Database $dbForProject, Event $queueForEvents) { $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $queueForEvents); $response diff --git a/app/worker.php b/app/worker.php index 0f93070eed..7f8f138af7 100644 --- a/app/worker.php +++ b/app/worker.php @@ -224,7 +224,7 @@ if (isset($args[0])) { try { $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => strtolower($workerName) === 'databases' ? 1 : swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'workersNum' => strtolower($workerName) === 'databases'? 1 :swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, ]); diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index 75b4ef573d..2321fe77dd 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -19,6 +19,7 @@ class Audit extends Event $this ->setQueue(Event::AUDITS_QUEUE_NAME) ->setClass(Event::BUILDS_CLASS_NAME); + } /** diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index a430a7fdc6..b48fc8f849 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -15,6 +15,7 @@ require_once __DIR__ . '/../../../app/init.php'; class EventTest extends TestCase { + protected ?Event $object = null; protected string $queue = ''; From 0ebb256b16199608e4353aca50a07b97ef9ef68f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 15:31:58 +1300 Subject: [PATCH 077/144] Revert formatting --- src/Appwrite/Platform/Workers/Databases.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index d4881c0f71..788924a238 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -481,7 +481,7 @@ class Databases extends Action ); } finally { $target = Realtime::fromPayload( - // Pass first, most verbose event pattern + // Pass first, most verbose event pattern event: $events[0], payload: $index, project: $project From 1ac3372239ba6579834758ecc67ee87d3734647d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 17:55:30 +1300 Subject: [PATCH 078/144] Set queue name per worker --- .env | 2 +- app/worker.php | 21 ++++++++++++--------- composer.json | 4 ++-- composer.lock | 12 ++++++------ docker-compose.yml | 8 ++++---- src/Appwrite/Event/Database.php | 6 +++--- 6 files changed, 28 insertions(+), 25 deletions(-) diff --git a/.env b/.env index 0e5da80955..d8e8b363f9 100644 --- a/.env +++ b/.env @@ -1,10 +1,10 @@ _APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=6 +_APP_CONNECTIONS_DB_QUEUE_01=database_db_main _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= -_APP_CONNECTIONS_DB_QUEUE=database-db-main _APP_SYSTEM_EMAIL_NAME=Appwrite _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io diff --git a/app/worker.php b/app/worker.php index 7f8f138af7..5c95fa67b6 100644 --- a/app/worker.php +++ b/app/worker.php @@ -18,13 +18,11 @@ use Swoole\Runtime; use Utopia\App; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; -use Utopia\CLI\CLI; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Queue\Adapter\Swoole; use Utopia\Platform\Service; use Utopia\Queue\Message; use Utopia\Queue\Server; @@ -134,9 +132,6 @@ Server::setResource('queueForMails', function (Connection $queue) { Server::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); -Server::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); Server::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); @@ -216,17 +211,25 @@ $pools = $register->get('pools'); $platform = new Appwrite(); $args = $_SERVER['argv']; -if (isset($args[0])) { - $workerName = end($args); -} else { +if (!isset($args[1])) { Console::error('Missing worker name'); + Console::exit(1); +} + +\array_shift($args); +$workerName = $args[0]; +$workerIndex = $args[1] ?? ''; + +if (!empty($workerNum)) { + $workerName .= '_' . $workerIndex; } try { $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => strtolower($workerName) === 'databases'? 1 :swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'workersNum' => str_starts_with(strtolower($workerName), 'databases') ? 1 : swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, + 'queueName' => App::getEnv('_APP_CONNECTIONS_DB_QUEUE', 'database_db_main') ]); } catch (\Exception $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); diff --git a/composer.json b/composer.json index b5d2392406..f7e4e56e04 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "utopia-php/image": "0.5.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", - "utopia-php/messaging": "0.1.*", + "utopia-php/messaging": "0.2.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "dev-integrate-workers as 0.3.3", @@ -64,7 +64,7 @@ "utopia-php/preloader": "0.2.*", "utopia-php/queue": "dev-feat-get-worker-start as 0.5.3", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.14.*", + "utopia-php/storage": "0.17.*", "utopia-php/swoole": "0.5.*", "utopia-php/vcs": "0.5.*", "utopia-php/websocket": "0.1.*", diff --git a/composer.lock b/composer.lock index 981cc546a1..1a13e56174 100644 --- a/composer.lock +++ b/composer.lock @@ -2007,12 +2007,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "f7fe90764f0bcf73eea6f968965b2d786822033b" + "reference": "056a60bb65eb12005f451714da0fe35a22f424c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/f7fe90764f0bcf73eea6f968965b2d786822033b", - "reference": "f7fe90764f0bcf73eea6f968965b2d786822033b", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/056a60bb65eb12005f451714da0fe35a22f424c2", + "reference": "056a60bb65eb12005f451714da0fe35a22f424c2", "shasum": "" }, "require": { @@ -2046,9 +2046,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/integrate-workers" + "source": "https://github.com/utopia-php/platform/tree/feat-custom-queue-names" }, - "time": "2023-10-04T14:44:45+00:00" + "time": "2023-10-12T04:31:27+00:00" }, { "name": "utopia-php/pools", @@ -5316,5 +5316,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/docker-compose.yml b/docker-compose.yml index fe41866ac3..50e95be6aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -356,10 +356,10 @@ services: - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - appwrite-worker-databases: - entrypoint: worker-databases + appwrite-worker-databases-01: + entrypoint: worker-databases 01 <<: *x-logging - container_name: appwrite-worker-databases + container_name: appwrite-worker-databases-01 image: appwrite-dev networks: - appwrite @@ -372,7 +372,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTIONS_DB_QUEUE + - _APP_CONNECTIONS_DB_QUEUE_01 - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 8670346c45..d9cbb141b8 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -18,9 +18,7 @@ class Database extends Event { parent::__construct($connection); - $this - ->setQueue($this->getProject()->getAttribute('database')) - ->setClass(Event::DATABASE_CLASS_NAME); + $this->setClass(Event::DATABASE_CLASS_NAME); } /** @@ -110,6 +108,8 @@ class Database extends Event */ public function trigger(): string|bool { + $this->setQueue($this->getProject()->getAttribute('database')); + $client = new Client($this->queue, $this->connection); return $client->enqueue([ From 6a05d9606e6d689c454be1c0b1ad35323169010b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 18:55:01 +1300 Subject: [PATCH 079/144] Remove default number postfix --- .env | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env b/.env index d8e8b363f9..0ecd29bb7b 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ _APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=6 -_APP_CONNECTIONS_DB_QUEUE_01=database_db_main +_APP_CONNECTIONS_DB_QUEUE=database_db_main _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/docker-compose.yml b/docker-compose.yml index 50e95be6aa..1ca0e0963c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,7 +372,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTIONS_DB_QUEUE_01 + - _APP_CONNECTIONS_DB_QUEUE - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From effdeb4bc20c6cbab450f8eb1ebeb12497bc8a75 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 19:10:45 +1300 Subject: [PATCH 080/144] Fix env vars --- app/worker.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/worker.php b/app/worker.php index 5c95fa67b6..d646a70218 100644 --- a/app/worker.php +++ b/app/worker.php @@ -226,10 +226,10 @@ if (!empty($workerNum)) { try { $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => str_starts_with(strtolower($workerName), 'databases') ? 1 : swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)), + 'workersNum' => App::getEnv('_APP_WORKERS_NUM', swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6))), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, - 'queueName' => App::getEnv('_APP_CONNECTIONS_DB_QUEUE', 'database_db_main') + 'queueName' => App::getEnv('_APP_CONNECTIONS_DB_QUEUE', strtolower($workerName)) ]); } catch (\Exception $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); From e2a83697122fbaf4833a6e5f5b81f4718d879d63 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 19:11:08 +1300 Subject: [PATCH 081/144] Add docblock for env vars --- app/worker.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/worker.php b/app/worker.php index d646a70218..ef58d5dfc6 100644 --- a/app/worker.php +++ b/app/worker.php @@ -225,6 +225,12 @@ if (!empty($workerNum)) { } try { + /** + * Any worker can be configured with the following env vars: + * - _APP_WORKERS_NUM The total number of worker processes + * - _APP_WORKER_PER_CORE The number of worker processes per core (ignored if _APP_WORKERS_NUM is set) + * - _APP_CONNECTIONS_DB_QUEUE The name of the queue to read for database events + */ $platform->init(Service::TYPE_WORKER, [ 'workersNum' => App::getEnv('_APP_WORKERS_NUM', swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6))), 'connection' => $pools->get('queue')->pop()->getResource(), From 855fe5b893c0de3a3b0c808e2a41f852e68efb1c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 19:12:26 +1300 Subject: [PATCH 082/144] Remove redundant postfix --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1ca0e0963c..3036226835 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -356,10 +356,10 @@ services: - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - appwrite-worker-databases-01: - entrypoint: worker-databases 01 + appwrite-worker-databases-: + entrypoint: worker-databases <<: *x-logging - container_name: appwrite-worker-databases-01 + container_name: appwrite-worker-databases image: appwrite-dev networks: - appwrite From 34e7af806aef4045835ce6bd0d23e1d8958d9c3b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 20:10:44 +1300 Subject: [PATCH 083/144] Fix typo --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3036226835..fe41866ac3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -356,7 +356,7 @@ services: - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - appwrite-worker-databases-: + appwrite-worker-databases: entrypoint: worker-databases <<: *x-logging container_name: appwrite-worker-databases From dc580c3d0d10fcf279d4d49ba151bc9edb4bf486 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 20:15:16 +1300 Subject: [PATCH 084/144] Change name --- .env | 2 +- app/worker.php | 4 ++-- docker-compose.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env b/.env index 0ecd29bb7b..c20299b429 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ _APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=6 -_APP_CONNECTIONS_DB_QUEUE=database_db_main +_APP_QUEUE_NAME=database_db_main _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/app/worker.php b/app/worker.php index ef58d5dfc6..a8e806ff93 100644 --- a/app/worker.php +++ b/app/worker.php @@ -229,13 +229,13 @@ try { * Any worker can be configured with the following env vars: * - _APP_WORKERS_NUM The total number of worker processes * - _APP_WORKER_PER_CORE The number of worker processes per core (ignored if _APP_WORKERS_NUM is set) - * - _APP_CONNECTIONS_DB_QUEUE The name of the queue to read for database events + * - _APP_QUEUE_NAME The name of the queue to read for database events */ $platform->init(Service::TYPE_WORKER, [ 'workersNum' => App::getEnv('_APP_WORKERS_NUM', swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6))), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, - 'queueName' => App::getEnv('_APP_CONNECTIONS_DB_QUEUE', strtolower($workerName)) + 'queueName' => App::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)) ]); } catch (\Exception $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); diff --git a/docker-compose.yml b/docker-compose.yml index fe41866ac3..d27dd99e31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,7 +372,7 @@ services: - mariadb environment: - _APP_ENV - - _APP_CONNECTIONS_DB_QUEUE + - _APP_QUEUE_NAME - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From 8c92170cb6f2a013483ec08242ebe595e95eeea6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 20:22:26 +1300 Subject: [PATCH 085/144] Add missing workers num --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index d27dd99e31..7f44d9f479 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -372,6 +372,7 @@ services: - mariadb environment: - _APP_ENV + - _APP_WORKERS_NUM=1 - _APP_QUEUE_NAME - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 From 204912cb98dbef7bf7a29407eab77b54c2cad548 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 20:59:40 +1300 Subject: [PATCH 086/144] Update compose phtml --- app/views/install/compose.phtml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 8928107029..0af24e8dc0 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -332,6 +332,8 @@ services: - mariadb environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_QUEUE_NAME - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From 48bc3691493f78d3f5aba71c4c6d4cd13e0d30c8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 12 Oct 2023 21:32:33 +1300 Subject: [PATCH 087/144] Hardcode default queue for db worker only --- .env | 1 - app/views/install/compose.phtml | 2 +- docker-compose.yml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.env b/.env index c20299b429..feeb74d849 100644 --- a/.env +++ b/.env @@ -1,7 +1,6 @@ _APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=6 -_APP_QUEUE_NAME=database_db_main _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 0af24e8dc0..8deec9e690 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -333,7 +333,7 @@ services: environment: - _APP_ENV - _APP_WORKERS_NUM=1 - - _APP_QUEUE_NAME + - _APP_QUEUE_NAME=database_db_main - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/docker-compose.yml b/docker-compose.yml index 7f44d9f479..c09a1aab6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -373,7 +373,7 @@ services: environment: - _APP_ENV - _APP_WORKERS_NUM=1 - - _APP_QUEUE_NAME + - _APP_QUEUE_NAME=database_db_main - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST From 4088941f706ab85a1a76449ae7fa52039674a6be Mon Sep 17 00:00:00 2001 From: shimon Date: Thu, 12 Oct 2023 16:34:13 +0300 Subject: [PATCH 088/144] sync with main --- app/cli.php | 7 +++---- app/worker.php | 1 + composer.lock | 44 +++++++++++++++++++++----------------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/app/cli.php b/app/cli.php index 0d70faaaec..74839f2de9 100644 --- a/app/cli.php +++ b/app/cli.php @@ -23,9 +23,7 @@ use Utopia\Registry\Registry; Authorization::disable(); -global $register; - -CLI::setResource('register', fn () => $register); +CLI::setResource('register', fn()=>$register); CLI::setResource('cache', function ($pools) { $list = Config::getParam('pools-cache', []); @@ -35,7 +33,8 @@ CLI::setResource('cache', function ($pools) { $adapters[] = $pools ->get($value) ->pop() - ->getResource(); + ->getResource() + ; } return new Cache(new Sharding($adapters)); diff --git a/app/worker.php b/app/worker.php index 7f8f138af7..977ab74d02 100644 --- a/app/worker.php +++ b/app/worker.php @@ -238,6 +238,7 @@ $worker ->shutdown() ->inject('pools') ->action(function (Group $pools) { + var_dump('reclaiming connection'); $pools->reclaim(); }); diff --git a/composer.lock b/composer.lock index 981cc546a1..3aedfb350d 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": "748f1a5e4fdf0a73ea26ddf0e7822f19", + "content-hash": "509d6d460bad65092c5e3b8d6b390ec9", "packages": [ { "name": "adhocore/jwt", @@ -1797,16 +1797,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.2.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", "shasum": "" }, "require": { @@ -1815,8 +1815,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" }, "type": "library", "autoload": { @@ -1839,9 +1839,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" }, - "time": "2023-09-14T20:48:42+00:00" + "time": "2023-02-07T05:42:46+00:00" }, { "name": "utopia-php/migration", @@ -2007,12 +2007,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "f7fe90764f0bcf73eea6f968965b2d786822033b" + "reference": "056a60bb65eb12005f451714da0fe35a22f424c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/f7fe90764f0bcf73eea6f968965b2d786822033b", - "reference": "f7fe90764f0bcf73eea6f968965b2d786822033b", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/056a60bb65eb12005f451714da0fe35a22f424c2", + "reference": "056a60bb65eb12005f451714da0fe35a22f424c2", "shasum": "" }, "require": { @@ -2046,9 +2046,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/integrate-workers" + "source": "https://github.com/utopia-php/platform/tree/feat-custom-queue-names" }, - "time": "2023-10-04T14:44:45+00:00" + "time": "2023-10-12T04:31:27+00:00" }, { "name": "utopia-php/pools", @@ -2269,16 +2269,16 @@ }, { "name": "utopia-php/storage", - "version": "0.17.0", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", "shasum": "" }, "require": { @@ -2286,12 +2286,10 @@ "ext-fileinfo": "*", "ext-lz4": "*", "ext-snappy": "*", - "ext-xz": "*", "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" + "utopia-php/framework": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2318,9 +2316,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.17.0" + "source": "https://github.com/utopia-php/storage/tree/0.14.0" }, - "time": "2023-08-21T11:28:36+00:00" + "time": "2023-03-15T00:16:34+00:00" }, { "name": "utopia-php/swoole", From b3d899873510fe11e4d95f8436c7b1527c81eb3d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 13 Oct 2023 12:29:03 +0400 Subject: [PATCH 089/144] chore: update hamster script --- src/Appwrite/Platform/Tasks/CalcTierStats.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php index 1678c6c621..2a2bc20af9 100644 --- a/src/Appwrite/Platform/Tasks/CalcTierStats.php +++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php @@ -155,12 +155,12 @@ class CalcTierStats extends Action } /** Get Usage stats */ - $range = '90d'; + $range = '30d'; $periods = [ - '90d' => [ + '30d' => [ 'period' => '1d', - 'limit' => 90, - ], + 'limit' => 30, + ] ]; $tmp = []; From d8714ec3fa60ddd77dcec2b5854dcd40ae1230b1 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 13 Oct 2023 12:54:10 +0400 Subject: [PATCH 090/144] fix: rename domains collection to rules in hamster script --- src/Appwrite/Platform/Tasks/CalcTierStats.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php index 2a2bc20af9..b614f4c8be 100644 --- a/src/Appwrite/Platform/Tasks/CalcTierStats.php +++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php @@ -200,8 +200,9 @@ class CalcTierStats extends Action try { /** Get Domains */ - $stats['Domains'] = $dbForConsole->count('domains', [ + $stats['Domains'] = $dbForConsole->count('rules', [ Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(APP_LIMIT_COUNT) ]); } catch (\Throwable) { $stats['Domains'] = 0; From 94178f8eaf09df8d0af17d1c0d1027eed6a56d75 Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 15 Oct 2023 20:41:09 +0300 Subject: [PATCH 091/144] rollback to Usage --- .env | 2 + .gitmodules | 2 +- Dockerfile | 1 - app/cli.php | 31 +- app/config/collections.php | 13 +- app/controllers/api/account.php | 31 +- app/controllers/api/databases.php | 481 +- app/controllers/api/functions.php | 315 +- app/controllers/api/project.php | 152 +- app/controllers/api/projects.php | 120 +- app/controllers/api/storage.php | 297 +- app/controllers/api/teams.php | 4 +- app/controllers/api/users.php | 169 +- app/controllers/shared/api.php | 174 +- app/init.php | 35 +- app/views/install/compose.phtml | 2 - app/worker.php | 10 +- bin/worker-usage | 3 - composer.json | 2 + composer.lock | 5318 ----------------- docker-compose.yml | 84 +- src/Appwrite/Migration/Migration.php | 1 + src/Appwrite/Platform/Services/Tasks.php | 2 + src/Appwrite/Platform/Services/Workers.php | 4 - src/Appwrite/Platform/Tasks/Maintenance.php | 2 +- src/Appwrite/Platform/Tasks/Usage.php | 60 + src/Appwrite/Platform/Workers/Builds.php | 38 +- src/Appwrite/Platform/Workers/Functions.php | 52 +- src/Appwrite/Platform/Workers/Usage.php | 244 - src/Appwrite/Platform/Workers/UsageHook.php | 105 - src/Appwrite/Usage/Calculator.php | 15 + src/Appwrite/Usage/Calculators/TimeSeries.php | 557 ++ src/Appwrite/Usage/Stats.php | 225 + .../Utopia/Response/Model/Migration.php | 1 + .../Utopia/Response/Model/UsageBuckets.php | 30 +- .../Utopia/Response/Model/UsageCollection.php | 30 +- .../Utopia/Response/Model/UsageDatabase.php | 62 +- .../Utopia/Response/Model/UsageDatabases.php | 92 +- .../Utopia/Response/Model/UsageFunction.php | 50 +- .../Utopia/Response/Model/UsageFunctions.php | 47 +- .../Utopia/Response/Model/UsageProject.php | 14 +- .../Utopia/Response/Model/UsageStorage.php | 66 +- .../Utopia/Response/Model/UsageUsers.php | 47 +- src/Executor/Executor.php | 13 +- tests/e2e/Services/Avatars/AvatarsBase.php | 2 +- .../Databases/DatabasesConsoleClientTest.php | 47 +- .../Databases/DatabasesCustomClientTest.php | 1 + .../Functions/FunctionsConsoleClientTest.php | 13 +- .../Functions/FunctionsCustomServerTest.php | 125 +- tests/e2e/Services/GraphQL/AvatarsTest.php | 2 +- .../Health/HealthCustomServerTest.php | 157 +- .../Projects/ProjectsConsoleClientTest.php | 16 +- .../Storage/StorageConsoleClientTest.php | 15 +- tests/e2e/Services/Teams/TeamsBaseServer.php | 2 +- tests/e2e/Services/Users/UsersBase.php | 12 + .../Services/Users/UsersConsoleClientTest.php | 43 +- tests/resources/docker/docker-compose.yml | 2 + tests/unit/Usage/StatsTest.php | 79 +- 58 files changed, 2931 insertions(+), 6588 deletions(-) delete mode 100644 bin/worker-usage delete mode 100644 composer.lock create mode 100644 src/Appwrite/Platform/Tasks/Usage.php delete mode 100644 src/Appwrite/Platform/Workers/Usage.php delete mode 100644 src/Appwrite/Platform/Workers/UsageHook.php create mode 100644 src/Appwrite/Usage/Calculator.php create mode 100644 src/Appwrite/Usage/Calculators/TimeSeries.php create mode 100644 src/Appwrite/Usage/Stats.php diff --git a/.env b/.env index feeb74d849..89f5980a44 100644 --- a/.env +++ b/.env @@ -1,6 +1,8 @@ _APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=6 +_APP_WORKERS_NUM=1 +_APP_QUEUE_NAME=database_db_main _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/.gitmodules b/.gitmodules index effe15f68a..8ec131892e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = feat-usage-sh-1.4 + branch = 3.2.1 diff --git a/Dockerfile b/Dockerfile index dd13341dcc..8cea481543 100755 --- a/Dockerfile +++ b/Dockerfile @@ -94,7 +94,6 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/worker-mails && \ chmod +x /usr/local/bin/worker-messaging && \ chmod +x /usr/local/bin/worker-webhooks && \ - chmod +x /usr/local/bin/worker-usage && \ chmod +x /usr/local/bin/worker-migrations # Cloud Executabless diff --git a/app/cli.php b/app/cli.php index 74839f2de9..1903c81e69 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,6 +5,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Delete; use Appwrite\Event\Certificate; +use Appwrite\Event\Func; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; use Utopia\Database\Validator\Authorization; @@ -23,6 +24,8 @@ use Utopia\Registry\Registry; Authorization::disable(); +global $register; + CLI::setResource('register', fn()=>$register); CLI::setResource('cache', function ($pools) { @@ -116,10 +119,36 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, }; }, ['pools', 'dbForConsole', 'cache']); +CLI::setResource('influxdb', function (Registry $register) { + $client = $register->get('influxdb'); /** @var InfluxDB\Client $client */ + $attempts = 0; + $max = 10; + $sleep = 1; + + do { // check if telegraf database is ready + try { + $attempts++; + $database = $client->selectDB('telegraf'); + if (in_array('telegraf', $client->listDatabases())) { + break; // leave the do-while if successful + } + } catch (\Throwable $th) { + Console::warning("InfluxDB not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('InfluxDB database not ready yet'); + } + sleep($sleep); + } + } while ($attempts < $max); + return $database; +}, ['register']); + CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); - +CLI::setResource('queueForFunctions', function (Group $pools) { + return new Func($pools->get('queue')->pop()->getResource()); +}, ['pools']); CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); diff --git a/app/config/collections.php b/app/config/collections.php index ef21b167df..db229ce87a 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1302,7 +1302,7 @@ $commonCollections = [ 'type' => Database::VAR_INTEGER, 'format' => '', 'size' => 8, - 'signed' => true, + 'signed' => false, 'required' => true, 'default' => null, 'array' => false, @@ -1330,6 +1330,17 @@ $commonCollections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('type'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 1, + 'signed' => false, + 'required' => true, + 'default' => 0, // 0 -> count, 1 -> sum + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 93df84a6f6..4494208078 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -58,6 +58,7 @@ App::post('/v1/account') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'create') @@ -173,6 +174,8 @@ App::post('/v1/account/sessions/email') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') + ->label('usage.metric', 'sessions.{scope}.requests.create') + ->label('usage.params', ['provider:email']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createEmailSession') @@ -425,6 +428,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') ->label('docs', false) + ->label('usage.metric', 'sessions.{scope}.requests.create') + ->label('usage.params', ['provider:{request.provider}']) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->param('state', '', new Text(2048), 'OAuth2 state params.', true) @@ -789,6 +794,7 @@ App::get('/v1/account/identities') ->desc('List Identities') ->groups(['api', 'account']) ->label('scope', 'account') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'listIdentities') @@ -843,6 +849,7 @@ App::delete('/v1/account/identities/:identityId') ->label('audits.event', 'identity.delete') ->label('audits.resource', 'identity/{request.$identityId}') ->label('audits.userId', '{user.$id}') + ->label('usage.metric', 'identities.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteIdentity') @@ -1091,6 +1098,8 @@ App::put('/v1/account/sessions/magic-url') ->label('audits.event', 'session.update') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') + ->label('usage.metric', 'sessions.{scope}.requests.create') + ->label('usage.params', ['provider:magic-url']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateMagicURLSession') @@ -1110,7 +1119,7 @@ App::put('/v1/account/sessions/magic-url') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Request $request, Response $response,Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + ->action(function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { /** @var Utopia\Database\Document $user */ @@ -1464,6 +1473,8 @@ App::post('/v1/account/sessions/anonymous') ->label('audits.event', 'session.create') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') + ->label('usage.metric', 'sessions.{scope}.requests.create') + ->label('usage.params', ['provider:anonymous']) ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createAnonymousSession') @@ -1662,6 +1673,7 @@ App::get('/v1/account/prefs') ->desc('Get account preferences') ->groups(['api', 'account']) ->label('scope', 'account') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'getPrefs') @@ -1723,6 +1735,7 @@ App::get('/v1/account/logs') ->desc('List logs') ->groups(['api', 'account']) ->label('scope', 'account') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'listLogs') @@ -1783,6 +1796,7 @@ App::get('/v1/account/sessions/:sessionId') ->desc('Get session') ->groups(['api', 'account']) ->label('scope', 'account') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'getSession') @@ -1830,6 +1844,7 @@ App::patch('/v1/account/name') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateName') @@ -1864,6 +1879,7 @@ App::patch('/v1/account/password') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePassword') @@ -1929,6 +1945,7 @@ App::patch('/v1/account/email') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateEmail') @@ -1998,6 +2015,7 @@ App::patch('/v1/account/phone') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhone') @@ -2056,6 +2074,7 @@ App::patch('/v1/account/prefs') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePrefs') @@ -2089,6 +2108,7 @@ App::patch('/v1/account/status') ->label('scope', 'account') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateStatus') @@ -2132,6 +2152,7 @@ App::delete('/v1/account/sessions/:sessionId') ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{user.$id}') + ->label('usage.metric', 'sessions.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteSession') @@ -2208,6 +2229,7 @@ App::patch('/v1/account/sessions/:sessionId') ->label('audits.event', 'session.update') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') + ->label('usage.metric', 'sessions.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateSession') @@ -2292,6 +2314,7 @@ App::delete('/v1/account/sessions') ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{user.$id}') + ->label('usage.metric', 'sessions.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteSessions') @@ -2353,6 +2376,7 @@ App::post('/v1/account/recovery') ->label('audits.event', 'recovery.create') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createRecovery') @@ -2535,6 +2559,7 @@ App::put('/v1/account/recovery') ->label('audits.event', 'recovery.update') ->label('audits.resource', 'user/{response.userId}') ->label('audits.userId', '{response.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateRecovery') @@ -2621,6 +2646,7 @@ App::post('/v1/account/verification') ->label('event', 'users.[userId].verification.[tokenId].create') ->label('audits.event', 'verification.create') ->label('audits.resource', 'user/{response.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createVerification') @@ -2781,6 +2807,7 @@ App::put('/v1/account/verification') ->label('event', 'users.[userId].verification.[tokenId].update') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{response.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateVerification') @@ -2841,6 +2868,7 @@ App::post('/v1/account/verification/phone') ->label('event', 'users.[userId].verification.[tokenId].create') ->label('audits.event', 'verification.create') ->label('audits.resource', 'user/{response.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createPhoneVerification') @@ -2936,6 +2964,7 @@ App::put('/v1/account/verification/phone') ->label('event', 'users.[userId].verification.[tokenId].update') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{response.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhoneVerification') diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index b4709e656e..9b92d8fa89 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -18,6 +18,7 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; +use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict; @@ -387,6 +388,7 @@ App::post('/v1/databases') ->label('scope', 'databases.write') ->label('audits.event', 'database.create') ->label('audits.resource', 'database/{response.$id}') + ->label('usage.metric', 'databases.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'create') @@ -460,6 +462,7 @@ App::get('/v1/databases') ->desc('List databases') ->groups(['api', 'database']) ->label('scope', 'databases.read') + ->label('usage.metric', 'databases.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'list') @@ -507,6 +510,7 @@ App::get('/v1/databases/:databaseId') ->desc('Get database') ->groups(['api', 'database']) ->label('scope', 'databases.read') + ->label('usage.metric', 'databases.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'get') @@ -621,6 +625,7 @@ App::put('/v1/databases/:databaseId') ->label('event', 'databases.[databaseId].update') ->label('audits.event', 'database.update') ->label('audits.resource', 'database/{response.$id}') + ->label('usage.metric', 'databases.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'update') @@ -665,6 +670,7 @@ App::delete('/v1/databases/:databaseId') ->label('event', 'databases.[databaseId].delete') ->label('audits.event', 'database.delete') ->label('audits.resource', 'database/{request.databaseId}') + ->label('usage.metric', 'databases.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'delete') @@ -709,6 +715,8 @@ App::post('/v1/databases/:databaseId/collections') ->label('scope', 'collections.write') ->label('audits.event', 'collection.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}') + ->label('usage.metric', 'collections.{scope}.requests.create') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createCollection') @@ -774,6 +782,8 @@ App::get('/v1/databases/:databaseId/collections') ->desc('List collections') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'listCollections') @@ -831,6 +841,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') ->desc('Get collection') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'getCollection') @@ -865,6 +877,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->desc('List collection logs') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'listCollectionLogs') @@ -962,6 +976,8 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->label('event', 'databases.[databaseId].collections.[collectionId].update') ->label('audits.event', 'collection.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateCollection') @@ -1030,6 +1046,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') ->label('event', 'databases.[databaseId].collections.[collectionId].delete') ->label('audits.event', 'collection.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.delete') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteCollection') @@ -1084,6 +1102,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createStringAttribute') @@ -1140,6 +1160,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createEmailAttribute') @@ -1182,6 +1204,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createEnumAttribute') @@ -1239,6 +1263,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createIpAttribute') @@ -1281,6 +1307,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createUrlAttribute') @@ -1323,6 +1351,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createIntegerAttribute') @@ -1394,6 +1424,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createFloatAttribute') @@ -1468,6 +1500,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createBooleanAttribute') @@ -1509,6 +1543,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createDatetimeAttribute') @@ -1553,6 +1589,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati ->label('scope', 'collections.write') ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createRelationshipAttribute') @@ -1630,6 +1668,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->desc('List attributes') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'listAttributes') @@ -1703,6 +1743,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->desc('Get attribute') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'getAttribute') @@ -1780,6 +1822,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateStringAttribute') @@ -2223,6 +2267,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete') ->label('audits.event', 'attribute.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteAttribute') @@ -2333,6 +2379,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->label('scope', 'collections.write') ->label('audits.event', 'index.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createIndex') @@ -2488,6 +2536,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->desc('List indexes') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'listIndexes') @@ -2551,6 +2601,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->desc('Get index') ->groups(['api', 'database']) ->label('scope', 'collections.read') + ->label('usage.metric', 'collections.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'getIndex') @@ -2593,6 +2645,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete') ->label('audits.event', 'index.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'collections.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteIndex') @@ -2657,6 +2711,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->label('scope', 'documents.write') ->label('audits.event', 'document.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('usage.metric', 'documents.{scope}.requests.create') + ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -2893,6 +2949,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->desc('List documents') ->groups(['api', 'database']) ->label('scope', 'documents.read') + ->label('usage.metric', 'documents.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'listDocuments') @@ -3018,6 +3076,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->desc('Get document') ->groups(['api', 'database']) ->label('scope', 'documents.read') + ->label('usage.metric', 'documents.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'getDocument') @@ -3111,6 +3171,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->desc('List document logs') ->groups(['api', 'database']) ->label('scope', 'documents.read') + ->label('usage.metric', 'documents.{scope}.requests.read') + ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'listDocumentLogs') @@ -3213,6 +3275,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->label('scope', 'documents.write') ->label('audits.event', 'document.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') + ->label('usage.metric', 'documents.{scope}.requests.update') + ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -3441,6 +3505,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete') ->label('audits.event', 'document.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}') + ->label('usage.metric', 'documents.{scope}.requests.delete') + ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -3562,57 +3628,107 @@ App::get('/v1/databases/usage') ->inject('dbForProject') ->action(function (string $range, Response $response, Database $dbForProject) { - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_DATABASES, - METRIC_COLLECTIONS, - METRIC_DOCUMENTS, - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; + + $metrics = [ + 'databases.$all.count.total', + 'documents.$all.count.total', + 'collections.$all.count.total', + 'databases.$all.requests.create', + 'databases.$all.requests.read', + 'databases.$all.requests.update', + 'databases.$all.requests.delete', + 'collections.$all.requests.create', + 'collections.$all.requests.read', + 'collections.$all.requests.update', + 'collections.$all.requests.delete', + 'documents.$all.requests.create', + 'documents.$all.requests.read', + 'documents.$all.requests.update', + 'documents.$all.requests.delete' + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + // Added 3'rd level to Index [period, metric, time] because of order by. + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'databasesCount' => $stats['databases.$all.count.total'] ?? [], + 'documentsCount' => $stats['documents.$all.count.total'] ?? [], + 'collectionsCount' => $stats['collections.$all.count.total'] ?? [], + 'documentsCreate' => $stats['documents.$all.requests.create'] ?? [], + 'documentsRead' => $stats['documents.$all.requests.read'] ?? [], + 'documentsUpdate' => $stats['documents.$all.requests.update'] ?? [], + 'documentsDelete' => $stats['documents.$all.requests.delete'] ?? [], + 'collectionsCreate' => $stats['collections.$all.requests.create'] ?? [], + 'collectionsRead' => $stats['collections.$all.requests.read'] ?? [], + 'collectionsUpdate' => $stats['collections.$all.requests.update'] ?? [], + 'collectionsDelete' => $stats['collections.$all.requests.delete'] ?? [], + 'databasesCreate' => $stats['databases.$all.requests.create'] ?? [], + 'databasesRead' => $stats['databases.$all.requests.read'] ?? [], + 'databasesUpdate' => $stats['databases.$all.requests.update'] ?? [], + 'databasesDelete' => $stats['databases.$all.requests.delete'] ?? [], + ]); } - } - $response->dynamic(new Document([ - 'range' => $range, - 'databasesTotal' => $usage[$metrics[0]], - 'collectionsTotal' => $usage[$metrics[1]], - 'documentsTotal' => $usage[$metrics[2]], - ]), Response::MODEL_USAGE_DATABASES); + + $response->dynamic($usage, Response::MODEL_USAGE_DATABASES); }); App::get('/v1/databases/:databaseId/usage') @@ -3631,62 +3747,97 @@ App::get('/v1/databases/:databaseId/usage') ->inject('dbForProject') ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) { - $database = $dbForProject->getDocument('databases', $databaseId); - - if ($database->isEmpty()) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'collectionsTotal' => $usage[$metrics[0]], - 'documentsTotal' => $usage[$metrics[1]], - ]), Response::MODEL_USAGE_DATABASE); + $metrics = [ + 'collections.' . $databaseId . '.count.total', + 'collections.' . $databaseId . '.requests.create', + 'collections.' . $databaseId . '.requests.read', + 'collections.' . $databaseId . '.requests.update', + 'collections.' . $databaseId . '.requests.delete', + 'documents.' . $databaseId . '.count.total', + 'documents.' . $databaseId . '.requests.create', + 'documents.' . $databaseId . '.requests.read', + 'documents.' . $databaseId . '.requests.update', + 'documents.' . $databaseId . '.requests.delete' + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + // TODO@kodumbeats explore performance if query is ordered by time ASC + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'collectionsCount' => $stats["collections.{$databaseId}.count.total"] ?? [], + 'collectionsCreate' => $stats["collections.{$databaseId}.requests.create"] ?? [], + 'collectionsRead' => $stats["collections.{$databaseId}.requests.read"] ?? [], + 'collectionsUpdate' => $stats["collections.{$databaseId}.requests.update"] ?? [], + 'collectionsDelete' => $stats["collections.{$databaseId}.requests.delete"] ?? [], + 'documentsCount' => $stats["documents.{$databaseId}.count.total"] ?? [], + 'documentsCreate' => $stats["documents.{$databaseId}.requests.create"] ?? [], + 'documentsRead' => $stats["documents.{$databaseId}.requests.read"] ?? [], + 'documentsUpdate' => $stats["documents.{$databaseId}.requests.update"] ?? [], + 'documentsDelete' => $stats["documents.{$databaseId}.requests.delete"] ?? [], + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_DATABASE); }); App::get('/v1/databases/:databaseId/collections/:collectionId/usage') @@ -3715,52 +3866,84 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'documentsTotal' => $usage[$metrics[0]], - ]), Response::MODEL_USAGE_COLLECTION); + $metrics = [ + "documents.{$databaseId}/{$collectionId}.count.total", + "documents.{$databaseId}/{$collectionId}.requests.create", + "documents.{$databaseId}/{$collectionId}.requests.read", + "documents.{$databaseId}/{$collectionId}.requests.update", + "documents.{$databaseId}/{$collectionId}.requests.delete", + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'documentsCount' => $stats["documents.{$databaseId}/{$collectionId}.count.total"] ?? [], + 'documentsCreate' => $stats["documents.{$databaseId}/{$collectionId}.requests.create"] ?? [], + 'documentsRead' => $stats["documents.{$databaseId}/{$collectionId}.requests.read"] ?? [], + 'documentsUpdate' => $stats["documents.{$databaseId}/{$collectionId}.requests.update"] ?? [], + 'documentsDelete' => $stats["documents.{$databaseId}/{$collectionId}.requests.delete" ?? []] + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_COLLECTION); }); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 383adebe67..4a96308492 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -6,13 +6,13 @@ use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Func; -use Appwrite\Event\Usage; use Appwrite\Event\Validator\FunctionEvent; use Appwrite\Utopia\Response\Model\Rule; use Appwrite\Extend\Exception; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Messaging\Adapter\Realtime; use Utopia\Validator\Assoc; +use Appwrite\Usage\Stats; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -458,71 +458,97 @@ App::get('/v1/functions/:functionId/usage') throw new Exception(Exception::FUNCTION_NOT_FOUND); } - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS), - str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), - str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), - str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), - str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), - str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), - str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'deploymentsTotal' => $usage[$metrics[0]], - 'deploymentsStorage' => $usage[$metrics[1]], - 'buildsTotal' => $usage[$metrics[2]], - 'buildsStorage' => $usage[$metrics[3]], - 'buildsTime' => $usage[$metrics[4]], - 'executionsTotal' => $usage[$metrics[5]], - 'executionsTime' => $usage[$metrics[6]], - ]), Response::MODEL_USAGE_FUNCTION); + $metrics = [ + "executions.$functionId.compute.total", + "executions.$functionId.compute.success", + "executions.$functionId.compute.failure", + "executions.$functionId.compute.time", + "builds.$functionId.compute.total", + "builds.$functionId.compute.success", + "builds.$functionId.compute.failure", + "builds.$functionId.compute.time", + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'executionsTotal' => $stats["executions.$functionId.compute.total"] ?? [], + 'executionsFailure' => $stats["executions.$functionId.compute.failure"] ?? [], + 'executionsSuccess' => $stats["executions.$functionId.compute.success"] ?? [], + 'executionsTime' => $stats["executions.$functionId.compute.time"] ?? [], + 'buildsTotal' => $stats["builds.$functionId.compute.total"] ?? [], + 'buildsFailure' => $stats["builds.$functionId.compute.failure"] ?? [], + 'buildsSuccess' => $stats["builds.$functionId.compute.success"] ?? [], + 'buildsTime' => $stats["builds.$functionId.compute.time" ?? []] + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_FUNCTION); }); App::get('/v1/functions/usage') ->desc('Get functions usage') - ->groups(['api', 'functions']) + ->groups(['api', 'functions', 'usage']) ->label('scope', 'functions.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'functions') @@ -535,67 +561,92 @@ App::get('/v1/functions/usage') ->inject('dbForProject') ->action(function (string $range, Response $response, Database $dbForProject) { - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_FUNCTIONS, - METRIC_DEPLOYMENTS, - METRIC_DEPLOYMENTS_STORAGE, - METRIC_BUILDS, - METRIC_BUILDS_STORAGE, - METRIC_BUILDS_COMPUTE, - METRIC_EXECUTIONS, - METRIC_EXECUTIONS_COMPUTE, - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; + + $metrics = [ + 'executions.$all.compute.total', + 'executions.$all.compute.failure', + 'executions.$all.compute.success', + 'executions.$all.compute.time', + 'builds.$all.compute.total', + 'builds.$all.compute.failure', + 'builds.$all.compute.success', + 'builds.$all.compute.time', + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'executionsTotal' => $stats[$metrics[0]] ?? [], + 'executionsFailure' => $stats[$metrics[1]] ?? [], + 'executionsSuccess' => $stats[$metrics[2]] ?? [], + 'executionsTime' => $stats[$metrics[3]] ?? [], + 'buildsTotal' => $stats[$metrics[4]] ?? [], + 'buildsFailure' => $stats[$metrics[5]] ?? [], + 'buildsSuccess' => $stats[$metrics[6]] ?? [], + 'buildsTime' => $stats[$metrics[7]] ?? [], + ]); } - } - $response->dynamic(new Document([ - 'range' => $range, - 'functionsTotal' => $usage[$metrics[0]], - 'deploymentsTotal' => $usage[$metrics[1]], - 'deploymentsStorage' => $usage[$metrics[2]], - 'buildsTotal' => $usage[$metrics[3]], - 'buildsStorage' => $usage[$metrics[4]], - 'buildsTime' => $usage[$metrics[5]], - 'executionsTotal' => $usage[$metrics[6]], - 'executionsTime' => $usage[$metrics[7]], - ]), Response::MODEL_USAGE_FUNCTIONS); + + $response->dynamic($usage, Response::MODEL_USAGE_FUNCTIONS); }); App::put('/v1/functions/:functionId') @@ -1002,7 +1053,7 @@ App::post('/v1/functions/:functionId/deployments') ->inject('deviceFunctions') ->inject('deviceLocal') ->inject('queueForBuilds') - ->action(function (string $functionId, mixed $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Build $queueForBuilds) { + ->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Build $queueForBuilds) { $activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN); @@ -1460,11 +1511,11 @@ App::post('/v1/functions/:functionId/executions') ->inject('dbForProject') ->inject('user') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('usage') ->inject('mode') ->inject('queueForFunctions') ->inject('geodb') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb) { + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Stats $usage, string $mode, Func $queueForFunctions, Reader $geodb) { $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); @@ -1662,7 +1713,8 @@ App::post('/v1/functions/:functionId/executions') path: $path, method: $method, headers: $headers, - runtimeEntrypoint: $command + runtimeEntrypoint: $command, + requestTimeout: 30 ); $headersFiltered = []; @@ -1680,8 +1732,6 @@ App::post('/v1/functions/:functionId/executions') $execution->setAttribute('logs', $executionResponse['logs']); $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); - - } catch (\Throwable $th) { $durationEnd = \microtime(true); @@ -1693,18 +1743,19 @@ App::post('/v1/functions/:functionId/executions') Console::error($th->getMessage()); } - $queueForUsage - ->addMetric(METRIC_EXECUTIONS, 1) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) - ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000))// per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000))// per function - ; - if ($function->getAttribute('logging')) { /** @var Document $execution */ $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); } + // TODO revise this later using route label + $usage + ->setParam('functionId', $function->getId()) + ->setParam('executions.{scope}.compute', 1) + ->setParam('executionStatus', $execution->getAttribute('status', '')) + ->setParam('executionTime', $execution->getAttribute('duration')); // ms + + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 0d0909fb0a..bd8d9d5f73 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -3,7 +3,6 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -15,10 +14,11 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; +use Utopia\Database\DateTime; App::get('/v1/project/usage') ->desc('Get usage stats for a project') - ->groups(['api', 'usage']) + ->groups(['api']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'project') @@ -30,76 +30,94 @@ App::get('/v1/project/usage') ->inject('response') ->inject('dbForProject') ->action(function (string $range, Response $response, Database $dbForProject) { - - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_NETWORK_REQUESTS, - METRIC_NETWORK_INBOUND, - METRIC_NETWORK_OUTBOUND, - METRIC_EXECUTIONS, - METRIC_DOCUMENTS, - METRIC_DATABASES, - METRIC_USERS, - METRIC_BUCKETS, - METRIC_FILES_STORAGE - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; + + $metrics = [ + 'project.$all.network.requests', + 'project.$all.network.bandwidth', + 'project.$all.storage.size', + 'users.$all.count.total', + 'databases.$all.count.total', + 'documents.$all.count.total', + 'executions.$all.compute.total', + 'buckets.$all.count.total' + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'requests' => $stats[$metrics[0]] ?? [], + 'network' => $stats[$metrics[1]] ?? [], + 'storage' => $stats[$metrics[2]] ?? [], + 'users' => $stats[$metrics[3]] ?? [], + 'databases' => $stats[$metrics[4]] ?? [], + 'documents' => $stats[$metrics[5]] ?? [], + 'executions' => $stats[$metrics[6]] ?? [], + 'buckets' => $stats[$metrics[7]] ?? [], + ]); } - } - - $response->dynamic(new Document([ - 'range' => $range, - 'requestsTotal' => ($usage[$metrics[0]]), - 'network' => ($usage[$metrics[1]] + $usage[$metrics[2]]), - 'executionsTotal' => $usage[$metrics[3]], - 'documentsTotal' => $usage[$metrics[4]], - 'databasesTotal' => $usage[$metrics[5]], - 'usersTotal' => $usage[$metrics[6]], - 'bucketsTotal' => $usage[$metrics[7]], - 'filesStorage' => $usage[$metrics[8]], - ]), Response::MODEL_USAGE_PROJECT); + $response->dynamic($usage, Response::MODEL_USAGE_PROJECT); }); - // Variables App::post('/v1/project/variables') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 8e35bc0dcc..0afdcf0a43 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -293,6 +293,120 @@ App::get('/v1/projects/:projectId') $response->dynamic($project, Response::MODEL_PROJECT); }); +App::get('/v1/projects/:projectId/usage') + ->desc('Get usage stats for a project') + ->groups(['api', 'projects', 'usage']) + ->label('scope', 'projects.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'projects') + ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USAGE_PROJECT) + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForConsole') + ->inject('dbForProject') + ->inject('register') + ->action(function (string $projectId, string $range, Response $response, Database $dbForConsole, Database $dbForProject, Registry $register) { + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + throw new Exception(Exception::PROJECT_NOT_FOUND); + } + + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + $dbForProject->setNamespace("_{$project->getInternalId()}"); + + $metrics = [ + 'project.$all.network.requests', + 'project.$all.network.bandwidth', + 'project.$all.storage.size', + 'users.$all.count.total', + 'databases.$all.count.total', + 'documents.$all.count.total', + 'executions.$all.compute.total', + 'buckets.$all.count.total' + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'requests' => $stats[$metrics[0]] ?? [], + 'network' => $stats[$metrics[1]] ?? [], + 'storage' => $stats[$metrics[2]] ?? [], + 'users' => $stats[$metrics[3]] ?? [], + 'databases' => $stats[$metrics[4]] ?? [], + 'documents' => $stats[$metrics[5]] ?? [], + 'executions' => $stats[$metrics[6]] ?? [], + 'buckets' => $stats[$metrics[7]] ?? [], + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_PROJECT); + }); + App::patch('/v1/projects/:projectId') ->desc('Update project') ->groups(['api', 'projects']) @@ -341,7 +455,7 @@ App::patch('/v1/projects/:projectId') }); App::patch('/v1/projects/:projectId/team') - ->desc('Update project team') + ->desc('Update Project Team') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -732,8 +846,7 @@ App::delete('/v1/projects/:projectId') $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) - ->setDocument($project) - ; + ->setDocument($project); if (!$dbForConsole->deleteDocument('projects', $projectId)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB'); @@ -996,6 +1109,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') }); // Keys + App::post('/v1/projects/:projectId/keys') ->desc('Create key') ->groups(['api', 'projects']) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 12fe618462..f27a6ed589 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -52,6 +52,7 @@ App::post('/v1/storage/buckets') ->label('event', 'buckets.[bucketId].create') ->label('audits.event', 'bucket.create') ->label('audits.resource', 'bucket/{response.$id}') + ->label('usage.metric', 'buckets.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'createBucket') @@ -147,6 +148,7 @@ App::get('/v1/storage/buckets') ->desc('List buckets') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') + ->label('usage.metric', 'buckets.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'listBuckets') @@ -195,6 +197,7 @@ App::get('/v1/storage/buckets/:bucketId') ->desc('Get bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') + ->label('usage.metric', 'buckets.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getBucket') @@ -223,6 +226,7 @@ App::put('/v1/storage/buckets/:bucketId') ->label('event', 'buckets.[bucketId].update') ->label('audits.event', 'bucket.update') ->label('audits.resource', 'bucket/{response.$id}') + ->label('usage.metric', 'buckets.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'updateBucket') @@ -290,6 +294,7 @@ App::delete('/v1/storage/buckets/:bucketId') ->label('audits.event', 'bucket.delete') ->label('event', 'buckets.[bucketId].delete') ->label('audits.resource', 'bucket/{request.bucketId}') + ->label('usage.metric', 'buckets.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'deleteBucket') @@ -332,6 +337,8 @@ App::post('/v1/storage/buckets/:bucketId/files') ->label('audits.event', 'file.create') ->label('event', 'buckets.[bucketId].files.[fileId].create') ->label('audits.resource', 'file/{response.$id}') + ->label('usage.metric', 'files.{scope}.requests.create') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -682,6 +689,8 @@ App::get('/v1/storage/buckets/:bucketId/files') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('usage.metric', 'files.{scope}.requests.read') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'listFiles') ->label('sdk.description', '/docs/references/storage/list-files.md') @@ -761,6 +770,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->groups(['api', 'storage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('usage.metric', 'files.{scope}.requests.read') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getFile') ->label('sdk.description', '/docs/references/storage/get-file.md') @@ -810,6 +821,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->label('cache', true) ->label('cache.resourceType', 'bucket/{request.bucketId}') ->label('cache.resource', 'file/{request.fileId}') + ->label('usage.metric', 'files.{scope}.requests.read') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getFilePreview') @@ -974,6 +987,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->desc('Get file for download') ->groups(['api', 'storage']) ->label('scope', 'files.read') + ->label('usage.metric', 'files.{scope}.requests.read') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getFileDownload') @@ -1115,6 +1130,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->desc('Get file for view') ->groups(['api', 'storage']) ->label('scope', 'files.read') + ->label('usage.metric', 'files.{scope}.requests.read') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getFileView') @@ -1269,6 +1286,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->label('event', 'buckets.[bucketId].files.[fileId].update') ->label('audits.event', 'file.update') ->label('audits.resource', 'file/{response.$id}') + ->label('usage.metric', 'files.{scope}.requests.update') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -1377,6 +1396,8 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->label('event', 'buckets.[bucketId].files.[fileId].delete') ->label('audits.event', 'file.delete') ->label('audits.resource', 'file/{request.fileId}') + ->label('usage.metric', 'files.{scope}.requests.delete') + ->label('usage.params', ['bucketId:{request.bucketId}']) ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) @@ -1468,7 +1489,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') App::get('/v1/storage/usage') ->desc('Get usage stats for storage') - ->groups(['api', 'storage']) + ->groups(['api', 'storage', 'usage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'storage') @@ -1481,63 +1502,104 @@ App::get('/v1/storage/usage') ->inject('dbForProject') ->action(function (string $range, Response $response, Database $dbForProject) { - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_BUCKETS, - METRIC_FILES, - METRIC_FILES_STORAGE, - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'bucketsTotal' => $usage[$metrics[0]], - 'filesTotal' => $usage[$metrics[1]], - 'filesStorage' => $usage[$metrics[2]], - ]), Response::MODEL_USAGE_STORAGE); + $metrics = [ + 'project.$all.storage.size', + 'buckets.$all.count.total', + 'buckets.$all.requests.create', + 'buckets.$all.requests.read', + 'buckets.$all.requests.update', + 'buckets.$all.requests.delete', + 'files.$all.storage.size', + 'files.$all.count.total', + 'files.$all.requests.create', + 'files.$all.requests.read', + 'files.$all.requests.update', + 'files.$all.requests.delete', + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'bucketsCount' => $stats['buckets.$all.count.total'], + 'bucketsCreate' => $stats['buckets.$all.requests.create'], + 'bucketsRead' => $stats['buckets.$all.requests.read'], + 'bucketsUpdate' => $stats['buckets.$all.requests.update'], + 'bucketsDelete' => $stats['buckets.$all.requests.delete'], + 'storage' => $stats['project.$all.storage.size'], + 'filesCount' => $stats['files.$all.count.total'], + 'filesCreate' => $stats['files.$all.requests.create'], + 'filesRead' => $stats['files.$all.requests.read'], + 'filesUpdate' => $stats['files.$all.requests.update'], + 'filesDelete' => $stats['files.$all.requests.delete'], + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_STORAGE); }); App::get('/v1/storage/:bucketId/usage') - ->desc('Get usage stats for storage bucket') - ->groups(['api', 'storage']) + ->desc('Get usage stats for a storage bucket') + ->groups(['api', 'storage', 'usage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'storage') @@ -1557,55 +1619,86 @@ App::get('/v1/storage/:bucketId/usage') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES), - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'filesTotal' => $usage[$metrics[0]], - 'filesStorage' => $usage[$metrics[1]], - ]), Response::MODEL_USAGE_BUCKETS); + $metrics = [ + "files.{$bucketId}.count.total", + "files.{$bucketId}.storage.size", + "files.{$bucketId}.requests.create", + "files.{$bucketId}.requests.read", + "files.{$bucketId}.requests.update", + "files.{$bucketId}.requests.delete", + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'filesCount' => $stats[$metrics[0]], + 'filesStorage' => $stats[$metrics[1]], + 'filesCreate' => $stats[$metrics[2]], + 'filesRead' => $stats[$metrics[3]], + 'filesUpdate' => $stats[$metrics[4]], + 'filesDelete' => $stats[$metrics[5]], + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_BUCKETS); }); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 99fcfeb38c..a32f79e44b 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -257,7 +257,7 @@ App::put('/v1/teams/:teamId') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp , Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); @@ -388,7 +388,7 @@ App::post('/v1/teams/:teamId/memberships') ->inject('queueForMails') ->inject('queueForMessaging') ->inject('queueForEvents') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, EventPhone $queueForMessaging, Event $queueForEvents) { + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index bd709ee272..fcaa2ff641 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -114,6 +114,7 @@ App::post('/v1/users') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'create') @@ -130,7 +131,7 @@ App::post('/v1/users') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response,Document $project, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $queueForEvents); $response @@ -145,6 +146,7 @@ App::post('/v1/users/bcrypt') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createBcryptUser') @@ -175,6 +177,7 @@ App::post('/v1/users/md5') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createMD5User') @@ -205,6 +208,7 @@ App::post('/v1/users/argon2') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createArgon2User') @@ -235,6 +239,7 @@ App::post('/v1/users/sha') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createSHAUser') @@ -272,6 +277,7 @@ App::post('/v1/users/phpass') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createPHPassUser') @@ -302,6 +308,7 @@ App::post('/v1/users/scrypt') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createScryptUser') @@ -345,6 +352,7 @@ App::post('/v1/users/scrypt-modified') ->label('scope', 'users.write') ->label('audits.event', 'user.create') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.create') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createScryptModifiedUser') @@ -375,6 +383,7 @@ App::get('/v1/users') ->desc('List users') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'list') @@ -423,6 +432,7 @@ App::get('/v1/users/:userId') ->desc('Get user') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'get') @@ -448,6 +458,7 @@ App::get('/v1/users/:userId/prefs') ->desc('Get user preferences') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'getPrefs') @@ -475,6 +486,7 @@ App::get('/v1/users/:userId/sessions') ->desc('List user sessions') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'listSessions') @@ -516,6 +528,7 @@ App::get('/v1/users/:userId/memberships') ->desc('List user memberships') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'listMemberships') @@ -555,6 +568,7 @@ App::get('/v1/users/:userId/logs') ->desc('List user logs') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'listLogs') @@ -636,6 +650,7 @@ App::get('/v1/users/identities') ->desc('List Identities') ->groups(['api', 'users']) ->label('scope', 'users.read') + ->label('usage.metric', 'users.{scope}.requests.read') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'listIdentities') @@ -688,6 +703,7 @@ App::patch('/v1/users/:userId/status') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateStatus') @@ -723,6 +739,7 @@ App::put('/v1/users/:userId/labels') ->label('scope', 'users.write') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateLabels') @@ -760,6 +777,7 @@ App::patch('/v1/users/:userId/verification/phone') ->label('scope', 'users.write') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePhoneVerification') @@ -796,6 +814,7 @@ App::patch('/v1/users/:userId/name') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateName') @@ -833,6 +852,7 @@ App::patch('/v1/users/:userId/password') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePassword') @@ -897,6 +917,7 @@ App::patch('/v1/users/:userId/email') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateEmail') @@ -952,6 +973,7 @@ App::patch('/v1/users/:userId/phone') ->label('scope', 'users.write') ->label('audits.event', 'user.update') ->label('audits.resource', 'user/{response.$id}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePhone') @@ -996,6 +1018,7 @@ App::patch('/v1/users/:userId/verification') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{request.userId}') ->label('audits.userId', '{request.userId}') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateEmailVerification') @@ -1028,6 +1051,7 @@ App::patch('/v1/users/:userId/prefs') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.prefs') ->label('scope', 'users.write') + ->label('usage.metric', 'users.{scope}.requests.update') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePrefs') @@ -1063,6 +1087,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') ->label('scope', 'users.write') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{request.userId}') + ->label('usage.metric', 'sessions.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteSession') @@ -1106,6 +1131,7 @@ App::delete('/v1/users/:userId/sessions') ->label('scope', 'users.write') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{user.$id}') + ->label('usage.metric', 'sessions.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteSessions') @@ -1148,6 +1174,7 @@ App::delete('/v1/users/:userId') ->label('scope', 'users.write') ->label('audits.event', 'user.delete') ->label('audits.resource', 'user/{request.userId}') + ->label('usage.metric', 'users.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'delete') @@ -1190,6 +1217,7 @@ App::delete('/v1/users/identities/:identityId') ->label('scope', 'users.write') ->label('audits.event', 'identity.delete') ->label('audits.resource', 'identity/{request.$identityId}') + ->label('usage.metric', 'users.{scope}.requests.delete') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteIdentity') @@ -1214,7 +1242,7 @@ App::delete('/v1/users/identities/:identityId') App::get('/v1/users/usage') ->desc('Get usage stats for the users API') - ->groups(['api', 'users']) + ->groups(['api', 'users', 'usage']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'users') @@ -1222,60 +1250,97 @@ App::get('/v1/users/usage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USAGE_USERS) - ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true) ->inject('response') ->inject('dbForProject') ->inject('register') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->action(function (string $range, string $provider, Response $response, Database $dbForProject) { - $periods = Config::getParam('usage', []); - $stats = $usage = []; - $days = $periods[$range]; - $metrics = [ - METRIC_USERS, - METRIC_SESSIONS, - ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $days['limit']; - $period = $days['period']; - $results = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - $stats[$metric] = []; - foreach ($results as $result) { - $stats[$metric][$result->getAttribute('time')] = [ - 'value' => $result->getAttribute('value'), - ]; - } - } - }); - - $format = match ($days['period']) { - '1h' => 'Y-m-d\TH:00:00.000P', - '1d' => 'Y-m-d\T00:00:00.000P', - }; - - foreach ($metrics as $metric) { - $usage[$metric] = []; - $leap = time() - ($days['limit'] * $days['factor']); - while ($leap < time()) { - $leap += $days['factor']; - $formatDate = date($format, $leap); - $usage[$metric][] = [ - 'value' => $stats[$metric][$formatDate]['value'] ?? 0, - 'date' => $formatDate, + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $periods = [ + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], ]; - } - } - $response->dynamic(new Document([ - 'range' => $range, - 'usersTotal' => $usage[$metrics[0]], - 'sessionsTotal' => $usage[$metrics[1]], - ]), Response::MODEL_USAGE_USERS); + $metrics = [ + 'users.$all.count.total', + 'users.$all.requests.create', + 'users.$all.requests.read', + 'users.$all.requests.update', + 'users.$all.requests.delete', + 'sessions.$all.requests.create', + 'sessions.$all.requests.delete', + "sessions.$provider.requests.create", + ]; + + $stats = []; + + Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $limit = $periods[$range]['limit']; + $period = $periods[$range]['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + // backfill metrics with empty values for graphs + $backfill = $limit - \count($requestDocs); + while ($backfill > 0) { + $last = $limit - $backfill - 1; // array index of last added metric + $diff = match ($period) { // convert period to seconds for unix timestamp math + '1h' => 3600, + '1d' => 86400, + }; + $stats[$metric][] = [ + 'value' => 0, + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), + ]; + $backfill--; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); + + $usage = new Document([ + 'range' => $range, + 'usersCount' => $stats['users.$all.count.total'] ?? [], + 'usersCreate' => $stats['users.$all.requests.create'] ?? [], + 'usersRead' => $stats['users.$all.requests.read'] ?? [], + 'usersUpdate' => $stats['users.$all.requests.update'] ?? [], + 'usersDelete' => $stats['users.$all.requests.delete'] ?? [], + 'sessionsCreate' => $stats['sessions.$all.requests.create'] ?? [], + 'sessionsProviderCreate' => $stats["sessions.$provider.requests.create"] ?? [], + 'sessionsDelete' => $stats['sessions.$all.requests.delete' ?? []] + ]); + } + + $response->dynamic($usage, Response::MODEL_USAGE_USERS); }); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 872f2902d1..388851faef 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -8,8 +8,8 @@ use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Extend\Exception; -use Appwrite\Event\Usage; use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Usage\Stats; use Appwrite\Utopia\Response; use Appwrite\Utopia\Request; use Utopia\App; @@ -48,95 +48,43 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar return $label; }; -$databaseListener = function (string $event, Document $document, Document $project, Usage $queueForUsage, Database $dbForProject) { - - $value = 1; +$databaseListener = function (string $event, Document $document, Stats $usage) { + $multiplier = 1; if ($event === Database::EVENT_DOCUMENT_DELETE) { - $value = -1; + $multiplier = -1; } - switch (true) { - case $document->getCollection() === 'teams': - $queueForUsage - ->addMetric(METRIC_TEAMS, $value); // per project + $collection = $document->getCollection(); + switch ($collection) { + case 'users': + $usage->setParam('users.{scope}.count.total', 1 * $multiplier); break; - case $document->getCollection() === 'users': - $queueForUsage - ->addMetric(METRIC_USERS, $value); // per project - if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); - } + case 'databases': + $usage->setParam('databases.{scope}.count.total', 1 * $multiplier); break; - case $document->getCollection() === 'sessions': // sessions - $queueForUsage - ->addMetric(METRIC_SESSIONS, $value); //per project + case 'buckets': + $usage->setParam('buckets.{scope}.count.total', 1 * $multiplier); break; - case $document->getCollection() === 'databases': // databases - $queueForUsage - ->addMetric(METRIC_DATABASES, $value); // per project - - if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); - } - break; - case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections - $parts = explode('_', $document->getCollection()); - $databaseInternalId = $parts[1] ?? 0; - $queueForUsage - ->addMetric(METRIC_COLLECTIONS, $value) // per project - ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database - ; - - if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); - } - break; - case str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_'): //documents - $parts = explode('_', $document->getCollection()); - $databaseInternalId = $parts[1] ?? 0; - $collectionInternalId = $parts[3] ?? 0; - $queueForUsage - ->addMetric(METRIC_DOCUMENTS, $value) // per project - ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), $value) // per database - ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), $value); // per collection - break; - case $document->getCollection() === 'buckets': //buckets - $queueForUsage - ->addMetric(METRIC_BUCKETS, $value); // per project - if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); - } - break; - case str_starts_with($document->getCollection(), 'bucket_'): // files - $parts = explode('_', $document->getCollection()); - $bucketInternalId = $parts[1]; - $queueForUsage - ->addMetric(METRIC_FILES, $value) // per project - ->addMetric(METRIC_FILES_STORAGE, $document->getAttribute('sizeOriginal') * $value) // per project - ->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES), $value) // per bucket - ->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES_STORAGE), $document->getAttribute('sizeOriginal') * $value); // per bucket - break; - case $document->getCollection() === 'functions': - $queueForUsage - ->addMetric(METRIC_FUNCTIONS, $value); // per project - - if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); - } - break; - case $document->getCollection() === 'deployments': - $queueForUsage - ->addMetric(METRIC_DEPLOYMENTS, $value) // per project - ->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value)// per function - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value); + case 'deployments': + $usage->setParam('deployments.{scope}.storage.size', $document->getAttribute('size') * $multiplier); break; default: + if (strpos($collection, 'bucket_') === 0) { + $usage + ->setParam('bucketId', $document->getAttribute('bucketId')) + ->setParam('files.{scope}.storage.size', $document->getAttribute('sizeOriginal') * $multiplier) + ->setParam('files.{scope}.count.total', 1 * $multiplier); + } elseif (strpos($collection, 'database_') === 0) { + $usage + ->setParam('databaseId', $document->getAttribute('databaseId')); + if (strpos($collection, '_collection_') !== false) { + $usage + ->setParam('collectionId', $document->getAttribute('$collectionId')) + ->setParam('documents.{scope}.count.total', 1 * $multiplier); + } else { + $usage->setParam('collections.{scope}.count.total', 1 * $multiplier); + } + } break; } }; @@ -155,8 +103,8 @@ App::init() ->inject('dbForProject') ->inject('mode') ->inject('queueForMails') - ->inject('queueForUsage') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, string $mode, Mail $queueForMails, Usage $queueForUsage) use ($databaseListener) { + ->inject('usage') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, string $mode, Mail $queueForMails, Stats $usage) use ($databaseListener) { $route = $utopia->getRoute(); @@ -238,14 +186,19 @@ App::init() ->setProject($project) ->setUser($user); + $usage + ->setParam('projectInternalId', $project->getInternalId()) + ->setParam('projectId', $project->getId()) + ->setParam('project.{scope}.network.requests', 1) + ->setParam('httpMethod', $request->getMethod()) + ->setParam('project.{scope}.network.inbound', 0) + ->setParam('project.{scope}.network.outbound', 0); $queueForDeletes->setProject($project); $queueForDatabase->setProject($project); - $dbForProject - ->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) - ->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) - ; + $dbForProject->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, Document $document) => $databaseListener($event, $document, $usage)); + $dbForProject->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, Document $document) => $databaseListener($event, $document, $usage)); $useCache = $route->getLabel('cache', false); @@ -409,14 +362,14 @@ App::shutdown() ->inject('user') ->inject('queueForEvents') ->inject('queueForAudits') - ->inject('queueForUsage') + ->inject('usage') ->inject('queueForDeletes') ->inject('queueForDatabase') ->inject('dbForProject') ->inject('queueForFunctions') ->inject('mode') ->inject('dbForConsole') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Stats $usage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -569,24 +522,35 @@ App::shutdown() } } + if ( + App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled' + && $project->getId() + && !empty($route->getLabel('sdk.namespace', null)) + ) { // Don't calculate console usage on admin mode + $metric = $route->getLabel('usage.metric', ''); + $usageParams = $route->getLabel('usage.params', []); - - if ($project->getId() !== 'console') { - if ($mode !== APP_MODE_ADMIN) { - $fileSize = 0; - $file = $request->getFiles('file'); - if (!empty($file)) { - $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; + if (!empty($metric)) { + $usage->setParam($metric, 1); + foreach ($usageParams as $param) { + $param = $parseLabel($param, $responsePayload, $requestParams, $user); + $parts = explode(':', $param); + if (count($parts) != 2) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Usage params not properly set'); + } + $usage->setParam($parts[0], $parts[1]); } - - $queueForUsage - ->addMetric(METRIC_NETWORK_REQUESTS, 1) - ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize) - ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()); } - $queueForUsage - ->setProject($project) - ->trigger(); + $fileSize = 0; + $file = $request->getFiles('file'); + if (!empty($file)) { + $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; + } + + $usage + ->setParam('project.{scope}.network.inbound', $request->getSize() + $fileSize) + ->setParam('project.{scope}.network.outbound', $response->getSize()) + ->submit(); } }); diff --git a/app/init.php b/app/init.php index be152f830b..f977394ba4 100644 --- a/app/init.php +++ b/app/init.php @@ -18,8 +18,8 @@ ini_set('display_startup_errors', 1); ini_set('default_socket_timeout', -1); error_reporting(E_ALL); -use Appwrite\Event\Migration; use Appwrite\Event\Usage; +use Appwrite\Event\Migration; use Appwrite\Extend\Exception; use Appwrite\Auth\Auth; use Appwrite\Event\Audit; @@ -33,6 +33,7 @@ use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Origin; use Appwrite\OpenSSL\OpenSSL; use Appwrite\URL\URL as AppwriteURL; +use Appwrite\Usage\Stats; use Utopia\App; use Utopia\Logger\Logger; use Utopia\Cache\Adapter\Redis as RedisCache; @@ -240,7 +241,6 @@ Config::load('platforms', __DIR__ . '/config/platforms.php'); Config::load('collections', __DIR__ . '/config/collections.php'); Config::load('runtimes', __DIR__ . '/config/runtimes.php'); Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); -Config::load('usage', __DIR__ . '/config/usage.php'); Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes Config::load('services', __DIR__ . '/config/services.php'); // List of services @@ -774,6 +774,31 @@ $register->set('pools', function () { return $group; }); +$register->set('influxdb', function () { + + // Register DB connection + $host = App::getEnv('_APP_INFLUXDB_HOST', ''); + $port = App::getEnv('_APP_INFLUXDB_PORT', ''); + + if (empty($host) || empty($port)) { + return; + } + $driver = new InfluxDB\Driver\Curl("http://{$host}:{$port}"); + $client = new InfluxDB\Client($host, $port, '', '', false, false, 5); + $client->setDriver($driver); + + return $client; +}); +$register->set('statsd', function () { + // Register DB connection + $host = App::getEnv('_APP_STATSD_HOST', 'telegraf'); + $port = App::getEnv('_APP_STATSD_PORT', 8125); + + $connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port); + $statsd = new \Domnikl\Statsd\Client($connection); + + return $statsd; +}); $register->set('smtp', function () { $mail = new PHPMailer(true); @@ -892,15 +917,15 @@ App::setResource('queueForAudits', function (Connection $queue) { App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); App::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); App::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); +App::setResource('usage', function ($register) { + return new Stats($register->get('statsd')); +}, ['register']); App::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 8deec9e690..8928107029 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -332,8 +332,6 @@ services: - mariadb environment: - _APP_ENV - - _APP_WORKERS_NUM=1 - - _APP_QUEUE_NAME=database_db_main - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/app/worker.php b/app/worker.php index f4e8f3f273..6995c2664e 100644 --- a/app/worker.php +++ b/app/worker.php @@ -10,10 +10,11 @@ use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\Mail; +use Appwrite\Event\Messaging; use Appwrite\Event\Migration; use Appwrite\Event\Phone; -use Appwrite\Event\Usage; use Appwrite\Platform\Appwrite; +use Appwrite\Usage\Stats; use Swoole\Runtime; use Utopia\App; use Utopia\Cache\Adapter\Sharding; @@ -147,9 +148,9 @@ Server::setResource('queueForFunctions', function (Connection $queue) { Server::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); -Server::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); +Server::setResource('usage', function ($register) { + return new Stats($register->get('statsd')); +}, ['register']); Server::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); @@ -247,7 +248,6 @@ $worker ->shutdown() ->inject('pools') ->action(function (Group $pools) { - var_dump('reclaiming connection'); $pools->reclaim(); }); diff --git a/bin/worker-usage b/bin/worker-usage deleted file mode 100644 index e39ce8477c..0000000000 --- a/bin/worker-usage +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -php /usr/src/code/app/worker.php usage $@ \ No newline at end of file diff --git a/composer.json b/composer.json index f7e4e56e04..9f79a5f414 100644 --- a/composer.json +++ b/composer.json @@ -71,10 +71,12 @@ "resque/php-resque": "1.3.6", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", + "influxdb/influxdb-php": "1.15.2", "phpmailer/phpmailer": "6.8.0", "chillerlan/php-qrcode": "4.3.4", "adhocore/jwt": "1.1.2", "webonyx/graphql-php": "14.11.*", + "slickdeals/statsd": "3.1.0", "league/csv": "9.7.1" }, "repositories": [ diff --git a/composer.lock b/composer.lock deleted file mode 100644 index a381097e67..0000000000 --- a/composer.lock +++ /dev/null @@ -1,5318 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "509d6d460bad65092c5e3b8d6b390ec9", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/appwrite", - "version": "10.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\": "src/Appwrite" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", - "support": { - "email": "team@appwrite.io", - "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", - "url": "https://appwrite.io/support" - }, - "time": "2023-09-07T23:28:31+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "f3897169f5c1f365312238a516ae9465f804634f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", - "reference": "f3897169f5c1f365312238a516ae9465f804634f", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" - }, - "time": "2023-02-24T09:50:42+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.13.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "5ab496b3908992b39275994a23783701c4b3de84" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.7.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2023-09-12T19:38:43+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1.4", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-25T09:12:45+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.2", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-09-10T18:51:20+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^0.12.66", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" - }, - "time": "2021-10-08T21:21:46+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "league/csv", - "version": "9.7.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/csv.git", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "ext-curl": "*", - "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^2.16", - "phpstan/phpstan": "^0.12.0", - "phpstan/phpstan-phpunit": "^0.12.0", - "phpstan/phpstan-strict-rules": "^0.12.0", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", - "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "League\\Csv\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://github.com/nyamsprod/", - "role": "Developer" - } - ], - "description": "CSV data manipulation made easy in PHP", - "homepage": "http://csv.thephpleague.com", - "keywords": [ - "convert", - "csv", - "export", - "filter", - "import", - "read", - "transform", - "write" - ], - "support": { - "docs": "https://csv.thephpleague.com", - "issues": "https://github.com/thephpleague/csv/issues", - "rss": "https://github.com/thephpleague/csv/releases.atom", - "source": "https://github.com/thephpleague/csv" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2021-04-17T16:32:08+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.1.6", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "5cbea85106e561c7138d03603eb6e05128480409" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", - "reference": "5cbea85106e561c7138d03603eb6e05128480409", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2023-10-02T10:01:54+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.11.0", - "jean85/pretty-package-versions": "^1.2 || ^2.0.1", - "php": "^7.1 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "squizlabs/php_codesniffer": "^3.6", - "symfony/phpunit-bridge": "^5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" - }, - "time": "2021-10-20T22:22:37+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.8.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", - "doctrine/annotations": "^1.2.6 || ^1.13.3", - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.1", - "yoast/phpunit-polyfills": "^1.0.4" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "ext-openssl": "Needed for secure SMTP sending and DKIM signing", - "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2023-03-06T14:43:22+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.31.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.31.1" - }, - "time": "2023-08-29T11:07:46+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.33.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.33.1" - }, - "time": "2023-08-29T11:07:40+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.43.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.3.*" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.4.*", - "pcov/clobber": "^2.0", - "phpstan/phpstan": "1.10.*", - "phpunit/phpunit": "^9.4", - "rregeer/phpunit-coverage-check": "^0.3.1", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistence using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.43.5" - }, - "time": "2023-10-06T06:49:47+00:00" - }, - { - "name": "utopia-php/domains", - "version": "0.3.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess Cope", - "email": "wess@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.3.2" - }, - "time": "2023-07-19T16:39:24+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.31.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.31.0" - }, - "time": "2023-08-30T16:10:04+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" - }, - "time": "2023-02-07T05:42:46+00:00" - }, - { - "name": "utopia-php/migration", - "version": "0.3.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/migration.git", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "shasum": "" - }, - "require": { - "appwrite/appwrite": "10.0.*", - "php": "8.*", - "utopia-php/cli": "0.*" - }, - "require-dev": { - "laravel/pint": "1.*", - "phpunit/phpunit": "9.*", - "vlucas/phpdotenv": "5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Migration\\": "src/Migration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to migrate resources between services.", - "keywords": [ - "framework", - "migration", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.3.5" - }, - "time": "2023-09-25T16:51:47+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.10.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.3.1" - }, - "time": "2023-09-01T17:25:28+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "dev-integrate-workers", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "056a60bb65eb12005f451714da0fe35a22f424c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/056a60bb65eb12005f451714da0fe35a22f424c2", - "reference": "056a60bb65eb12005f451714da0fe35a22f424c2", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "framework", - "php", - "platform", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/feat-custom-queue-names" - }, - "time": "2023-10-12T04:31:27+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "dev-feat-get-worker-start", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/5f81095e0ec66b2902037181ba3a20f6f9541af7", - "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" - }, - "time": "2023-10-04T14:49:13+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.14.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" - }, - "time": "2023-03-15T00:16:34+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.7.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.7.1" - }, - "time": "2023-08-30T09:14:37+00:00" - }, - { - "name": "utopia-php/vcs", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/vcs.git", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "shasum": "" - }, - "require": { - "adhocore/jwt": "^1.1", - "php": ">=8.0", - "utopia-php/cache": "^0.8.0", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\VCS\\": "src/VCS", - "Utopia\\Detector\\": "src/Detector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", - "keywords": [ - "framework", - "php", - "utopia", - "vcs" - ], - "support": { - "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.5.0" - }, - "time": "2023-09-13T19:05:52+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.10", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-07-05T14:23:37+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.35.2", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" - }, - "time": "2023-09-14T14:59:50+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" - }, - "time": "2023-09-27T20:04:15+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.71", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2023-04-25T20:33:03+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" - }, - "time": "2023-08-12T11:01:26+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.24.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bcad8d995980440892759db0c32acae7c8e79442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", - "reference": "bcad8d995980440892759db0c32acae7c8e79442", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" - }, - "time": "2023-09-26T12:28:12+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.29", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-19T04:57:46+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/5.0.2" - }, - "time": "2023-03-20T06:05:55+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-28T09:04:16+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.7.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-08-28T11:09:02+00:00" - } - ], - "aliases": [ - { - "package": "utopia-php/platform", - "version": "dev-integrate-workers", - "alias": "0.3.3", - "alias_normalized": "0.3.3.0" - }, - { - "package": "utopia-php/queue", - "version": "dev-feat-get-worker-start", - "alias": "0.5.3", - "alias_normalized": "0.5.3.0" - } - ], - "minimum-stability": "stable", - "stability-flags": { - "utopia-php/platform": 20, - "utopia-php/queue": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.3.0" -} diff --git a/docker-compose.yml b/docker-compose.yml index c09a1aab6e..f29b90d360 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -130,6 +130,8 @@ services: - _APP_SMTP_USERNAME - _APP_SMTP_PASSWORD - _APP_USAGE_STATS + - _APP_INFLUXDB_HOST + - _APP_INFLUXDB_PORT - _APP_STORAGE_LIMIT - _APP_STORAGE_PREVIEW_LIMIT - _APP_STORAGE_ANTIVIRUS @@ -166,6 +168,8 @@ services: - _APP_EXECUTOR_HOST - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG + - _APP_STATSD_HOST + - _APP_STATSD_PORT - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE @@ -372,8 +376,6 @@ services: - mariadb environment: - _APP_ENV - - _APP_WORKERS_NUM=1 - - _APP_QUEUE_NAME=database_db_main - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST @@ -387,6 +389,8 @@ services: - _APP_DB_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG + - _APP_WORKERS_NUM + - _APP_QUEUE_NAME appwrite-worker-builds: entrypoint: worker-builds @@ -651,10 +655,6 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE @@ -663,44 +663,39 @@ services: - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES - appwrite-worker-usage: - entrypoint: worker-usage + appwrite-usage: + entrypoint: usage <<: *x-logging - container_name: appwrite-worker-usage + container_name: appwrite-usage image: appwrite-dev networks: - appwrite volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform - - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue - + - ./dev:/usr/local/dev depends_on: - - redis + - influxdb - mariadb environment: - _APP_ENV - _APP_WORKER_PER_CORE - - _APP_CONNECTIONS_MAX - - _APP_POOL_CLIENTS - _APP_OPENSSL_KEY_V1 - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_INFLUXDB_HOST + - _APP_INFLUXDB_PORT + - _APP_USAGE_AGGREGATION_INTERVAL - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE - _APP_USAGE_STATS - - DOCKERHUB_PULL_USERNAME - - DOCKERHUB_PULL_PASSWORD + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG appwrite-schedule: entrypoint: schedule @@ -728,11 +723,6 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_CONNECTIONS_DB_PROJECT - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_CACHE - - _APP_CONNECTIONS_QUEUE - - _APP_REGION appwrite-assistant: container_name: appwrite-assistant @@ -868,6 +858,26 @@ services: # - appwrite # volumes: # - appwrite-uploads:/storage/uploads + + influxdb: + image: appwrite/influxdb:1.5.0 + container_name: appwrite-influxdb + <<: *x-logging + networks: + - appwrite + volumes: + - appwrite-influxdb:/var/lib/influxdb:rw + + telegraf: + image: appwrite/telegraf:1.4.0 + container_name: appwrite-telegraf + <<: *x-logging + networks: + - appwrite + environment: + - _APP_INFLUXDB_HOST + - _APP_INFLUXDB_PORT + # Dev Tools Start ------------------------------------------------------------------------------------------ # # The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack @@ -878,6 +888,7 @@ services: # RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks # RedisCommander - A nice UI for exploring Redis data # Resque - A nice UI for exploring Redis pub/sub, view the different queues workloads, pending and failed tasks + # Chronograf - A nice UI for exploring InfluxDB data # Webgrind - A nice UI for exploring and debugging code-level stuff maildev: # used mainly for dev tests @@ -931,6 +942,25 @@ services: # - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user # - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password + # chronograf: + # image: chronograf:1.6 + # container_name: appwrite-chronograf + # restart: unless-stopped + # networks: + # - appwrite + # volumes: + # - appwrite-chronograf:/var/lib/chronograf + # ports: + # - "8888:8888" + # environment: + # - INFLUXDB_URL=http://influxdb:8086 + # - KAPACITOR_URL=http://kapacitor:9092 + # - AUTH_DURATION=48h + # - TOKEN_SECRET=duperduper5674829!jwt + # - GH_CLIENT_ID=d86f7145a41eacfc52cc + # - GH_CLIENT_SECRET=9e0081062367a2134e7f2ea95ba1a32d08b6c8ab + # - GH_ORGS=appwrite + # webgrind: # image: 'jokkedk/webgrind:latest' # volumes: @@ -968,4 +998,6 @@ volumes: appwrite-certificates: appwrite-functions: appwrite-builds: + appwrite-influxdb: appwrite-config: + # appwrite-chronograf: diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 2d0597dd26..bb51e1bff0 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -69,6 +69,7 @@ abstract class Migration '1.4.2' => 'V19', '1.4.3' => 'V19', '1.4.4' => 'V19', + '1.4.5' => 'V19', ]; /** diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index 6645948e32..bc8d1bbc72 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -15,6 +15,7 @@ use Appwrite\Platform\Tasks\SSL; use Appwrite\Platform\Tasks\Hamster; use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute; use Appwrite\Platform\Tasks\ClearCardCache; +use Appwrite\Platform\Tasks\Usage; use Appwrite\Platform\Tasks\Vars; use Appwrite\Platform\Tasks\Version; use Appwrite\Platform\Tasks\VolumeSync; @@ -30,6 +31,7 @@ class Tasks extends Service $this->type = self::TYPE_CLI; $this ->addAction(Version::getName(), new Version()) + ->addAction(Usage::getName(), new Usage()) ->addAction(Vars::getName(), new Vars()) ->addAction(SSL::getName(), new SSL()) ->addAction(Hamster::getName(), new Hamster()) diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 67ecf3dd55..07fc25434e 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -12,8 +12,6 @@ use Appwrite\Platform\Workers\Databases; use Appwrite\Platform\Workers\Functions; use Appwrite\Platform\Workers\Builds; use Appwrite\Platform\Workers\Deletes; -use Appwrite\Platform\Workers\Usage; -use Appwrite\Platform\Workers\UsageHook; use Appwrite\Platform\Workers\Migrations; class Workers extends Service @@ -31,8 +29,6 @@ class Workers extends Service ->addAction(Functions::getName(), new Functions()) ->addAction(Builds::getName(), new Builds()) ->addAction(Deletes::getName(), new Deletes()) - ->addAction(UsageHook::getName(), new UsageHook()) - ->addAction(Usage::getName(), new Usage()) ->addAction(Migrations::getName(), new Migrations()) ; diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 214505ff74..f4756cd8a8 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -22,7 +22,7 @@ class Maintenance extends Action public function __construct() { $this - ->desc('Schedules maintenance tasks and publishes them to queues') + ->desc('Schedules maintenance tasks and publishes them to resque') ->inject('dbForConsole') ->inject('queueForCertificates') ->inject('queueForDeletes') diff --git a/src/Appwrite/Platform/Tasks/Usage.php b/src/Appwrite/Platform/Tasks/Usage.php new file mode 100644 index 0000000000..fa677ea142 --- /dev/null +++ b/src/Appwrite/Platform/Tasks/Usage.php @@ -0,0 +1,60 @@ +desc('Schedules syncing data from influxdb to Appwrite console db') + ->inject('dbForConsole') + ->inject('influxdb') + ->inject('register') + ->inject('getProjectDB') + ->inject('logError') + ->callback(fn ($dbForConsole, $influxDB, $register, $getProjectDB, $logError) => $this->action($dbForConsole, $influxDB, $register, $getProjectDB, $logError)); + } + + protected function aggregateTimeseries(UtopiaDatabase $database, InfluxDatabase $influxDB, callable $logError): void + { + } + + public function action(UtopiaDatabase $dbForConsole, InfluxDatabase $influxDB, Registry $register, callable $getProjectDB, callable $logError) + { + Console::title('Usage Aggregation V1'); + Console::success(APP_NAME . ' usage aggregation process v1 has started'); + + $errorLogger = fn(Throwable $error, string $action = 'syncUsageStats') => $logError($error, "usage", $action); + + $interval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '30'); // 30 seconds (by default) + $region = App::getEnv('region', 'default'); + $usage = new TimeSeries($region, $dbForConsole, $influxDB, $getProjectDB, $register, $errorLogger); + + Console::loop(function () use ($interval, $usage) { + $now = date('d-m-Y H:i:s', time()); + Console::info("[{$now}] Aggregating Timeseries Usage data every {$interval} seconds"); + $loopStart = microtime(true); + + $usage->collect(); + + $loopTook = microtime(true) - $loopStart; + $now = date('d-m-Y H:i:s', time()); + Console::info("[{$now}] Aggregation took {$loopTook} seconds"); + }, $interval); + } +} diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 3eb411d482..5a317d6ec6 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -6,6 +6,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Usage; use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Usage\Stats; use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Vcs\Comment; use Exception; @@ -47,11 +48,11 @@ class Builds extends Action ->inject('dbForConsole') ->inject('queueForEvents') ->inject('queueForFunctions') - ->inject('queueForUsage') + ->inject('usage') ->inject('cache') ->inject('getProjectDB') ->inject('getFunctionsDevice') - ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $queueForUsage, $cache, $getProjectDB, $getFunctionsDevice)); + ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Stats $usage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $getProjectDB, $getFunctionsDevice)); } /** @@ -59,14 +60,14 @@ class Builds extends Action * @param Database $dbForConsole * @param Event $queueForEvents * @param Func $queueForFunctions - * @param Usage $queueForUsage + * @param Stats $usage * @param Cache $cache * @param callable $getProjectDB * @param callable $getFunctionsDevice * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Stats $usage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice): void { $payload = $message->getPayload() ?? []; @@ -85,7 +86,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($getFunctionsDevice, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $getProjectDB, $github, $project, $resource, $deployment, $template); + $this->buildDeployment($getFunctionsDevice, $queueForFunctions, $queueForEvents, $usage, $dbForConsole, $getProjectDB, $github, $project, $resource, $deployment, $template); break; default: @@ -97,7 +98,7 @@ class Builds extends Action * @param callable $getFunctionsDevice * @param Func $queueForFunctions * @param Event $queueForEvents - * @param Usage $queueForUsage + * @param Stats $usage * @param Database $dbForConsole * @param callable $getProjectDB * @param GitHub $github @@ -109,7 +110,7 @@ class Builds extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(callable $getFunctionsDevice, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void + protected function buildDeployment(callable $getFunctionsDevice, Func $queueForFunctions, Event $queueForEvents, Stats $usage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); @@ -530,16 +531,19 @@ class Builds extends Action roles: $target['roles'] ); - /** Trigger usage queue */ - $queueForUsage - ->setProject($project) - ->addMetric(METRIC_BUILDS, 1) // per project - ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) - ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) - ->trigger(); + /** Update usage stats */ + if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { + $usage + ->setParam('projectInternalId', $project->getInternalId()) + ->setParam('projectId', $project->getId()) + ->setParam('functionId', $function->getId()) + ->setParam('builds.{scope}.compute', 1) + ->setParam('buildStatus', $build->getAttribute('status', '')) + ->setParam('buildTime', $build->getAttribute('duration')) + ->setParam('networkRequestSize', 0) + ->setParam('networkResponseSize', 0) + ->submit(); + } } } diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 328d3e41bf..d7607d626b 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -2,20 +2,20 @@ namespace Appwrite\Platform\Workers; +use Appwrite\Usage\Stats; use Appwrite\Event\Event; use Appwrite\Event\Func; -use Appwrite\Event\Usage; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; -use Throwable; use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -43,9 +43,9 @@ class Functions extends Action ->inject('dbForProject') ->inject('queueForFunctions') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('usage') ->inject('log') - ->callback(fn(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log)); + ->callback(fn(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Stats $usage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $usage, $log)); } /** @@ -53,13 +53,15 @@ class Functions extends Action * @param Database $dbForProject * @param Func $queueForFunctions * @param Event $queueForEvents - * @param Usage $queueForUsage + * @param Stats $usage * @param Log $log * @return void - * @throws Exception - * @throws Throwable + * @throws Authorization + * @throws Structure + * @throws \Utopia\Database\Exception + * @throws Conflict */ - public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log): void + public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Stats $usage, Log $log): void { $payload = $message->getPayload() ?? []; @@ -115,7 +117,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, + usage: $usage, queueForEvents: $queueForEvents, project: $project, function: $function, @@ -150,7 +152,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, + usage: $usage, queueForEvents: $queueForEvents, project: $project, function: $function, @@ -171,7 +173,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, + usage: $usage, queueForEvents: $queueForEvents, project: $project, function: $function, @@ -194,7 +196,7 @@ class Functions extends Action * @param Log $log * @param Database $dbForProject * @param Func $queueForFunctions - * @param Usage $queueForUsage + * @param Stats $usage * @param Event $queueForEvents * @param Document $project * @param Document $function @@ -212,13 +214,13 @@ class Functions extends Action * @throws Authorization * @throws Structure * @throws \Utopia\Database\Exception - * @throws \Utopia\Database\Exception\Conflict + * @throws Conflict */ private function execute( Log $log, Database $dbForProject, Func $queueForFunctions, - Usage $queueForUsage, + stats $usage, Event $queueForEvents, Document $project, Document $function, @@ -469,14 +471,18 @@ class Functions extends Action throw new Exception($error, $errorCode); } - /** Trigger usage queue */ - $queueForUsage - ->setProject($project) - ->addMetric(METRIC_EXECUTIONS, 1) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) - ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) - ->trigger() - ; + /** Update usage stats */ + if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { + $usage + ->setParam('projectId', $project->getId()) + ->setParam('projectInternalId', $project->getInternalId()) + ->setParam('functionId', $function->getId()) // TODO: We should use functionInternalId in usage stats + ->setParam('executions.{scope}.compute', 1) + ->setParam('executionStatus', $execution->getAttribute('status', '')) + ->setParam('executionTime', $execution->getAttribute('duration')) + ->setParam('networkRequestSize', 0) + ->setParam('networkResponseSize', 0) + ->submit(); + } } } diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php deleted file mode 100644 index 3afc3c5ae5..0000000000 --- a/src/Appwrite/Platform/Workers/Usage.php +++ /dev/null @@ -1,244 +0,0 @@ - 'Y-m-d H:00', - '1d' => 'Y-m-d 00:00', - 'inf' => '0000-00-00 00:00' - ]; - - const INFINITY_PERIOD = '_inf_'; - - public static function getName(): string - { - return 'usage'; - } - - /** - * @throws Exception - */ - public function __construct() - { - - $this - ->desc('Usage worker') - ->inject('message') - ->inject('pools') - ->inject('cache') - ->callback(function ($message, $pools, $cache) { - $this->action($message, $pools, $cache); - }); - } - - /** - * @param Message $message - * @param $pools - * @param $cache - * @return void - * @throws \Utopia\Database\Exception - */ - public function action(Message $message, $pools, $cache): void - { - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - $payload = $message->getPayload() ?? []; - $project = new Document($payload['project'] ?? []); - $projectId = $project->getInternalId(); - foreach ($payload['reduce'] ?? [] as $document) { - if (empty($document)) { - continue; - } - - $this->reduce( - database: $project->getAttribute('database'), - projectInternalId: $project->getInternalId(), - document: new Document($document), - metrics: $payload['metrics'], - pools: $pools, - cache: $cache - ); - } - - self::$stats[$projectId]['database'] = $project->getAttribute('database'); - foreach ($payload['metrics'] ?? [] as $metric) { - if (!isset(self::$stats[$projectId]['keys'][$metric['key']])) { - self::$stats[$projectId]['keys'][$metric['key']] = $metric['value']; - continue; - } - self::$stats[$projectId]['keys'][$metric['key']] += $metric['value']; - } - } - - - /** - * On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files. - * When we remove a parent document we need to deduct his children aggregation from the project scope. - - * @param $database - * @param $projectInternalId - * @param Document $document - * @param array $metrics - * @param $pools - * @param $cache - * @return void - */ - private function reduce($database, $projectInternalId, Document $document, array &$metrics, $pools, $cache) - { - try { - $dbForProject = new Database( - $pools - ->get($database) - ->pop() - ->getResource(), - $cache - ); - - $dbForProject->setNamespace('_' . $projectInternalId); - - switch (true) { - case $document->getCollection() === 'users': // users - $sessions = count($document->getAttribute(METRIC_SESSIONS, 0)); - if (!empty($sessions)) { - $metrics[] = [ - 'key' => METRIC_SESSIONS, - 'value' => ($sessions * -1), - ]; - } - break; - case $document->getCollection() === 'databases': // databases - $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); - $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS))); - if (!empty($collections['value'])) { - $metrics[] = [ - 'key' => METRIC_COLLECTIONS, - 'value' => ($collections['value'] * -1), - ]; - } - - if (!empty($documents['value'])) { - $metrics[] = [ - 'key' => METRIC_DOCUMENTS, - 'value' => ($documents['value'] * -1), - ]; - } - break; - case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections - $parts = explode('_', $document->getCollection()); - $databaseInternalId = $parts[1] ?? 0; - $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS))); - - if (!empty($documents['value'])) { - $metrics[] = [ - 'key' => METRIC_DOCUMENTS, - 'value' => ($documents['value'] * -1), - ]; - $metrics[] = [ - 'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), - 'value' => ($documents['value'] * -1), - ]; - } - break; - - case $document->getCollection() === 'buckets': - $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); - $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); - - if (!empty($files['value'])) { - $metrics[] = [ - 'key' => METRIC_FILES, - 'value' => ($files['value'] * -1), - ]; - } - - if (!empty($storage['value'])) { - $metrics[] = [ - 'key' => METRIC_FILES_STORAGE, - 'value' => ($storage['value'] * -1), - ]; - } - break; - - case $document->getCollection() === 'functions': - $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS))); - $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE))); - $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS))); - $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE))); - $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE))); - $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS))); - $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE))); - - if (!empty($deployments['value'])) { - $metrics[] = [ - 'key' => METRIC_DEPLOYMENTS, - 'value' => ($deployments['value'] * -1), - ]; - } - - if (!empty($deploymentsStorage['value'])) { - $metrics[] = [ - 'key' => METRIC_DEPLOYMENTS_STORAGE, - 'value' => ($deploymentsStorage['value'] * -1), - ]; - } - - if (!empty($builds['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS, - 'value' => ($builds['value'] * -1), - ]; - } - - if (!empty($buildsStorage['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_STORAGE, - 'value' => ($buildsStorage['value'] * -1), - ]; - } - - if (!empty($buildsCompute['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_COMPUTE, - 'value' => ($buildsCompute['value'] * -1), - ]; - } - - if (!empty($executions['value'])) { - $metrics[] = [ - 'key' => METRIC_EXECUTIONS, - 'value' => ($executions['value'] * -1), - ]; - } - - if (!empty($executionsCompute['value'])) { - $metrics[] = [ - 'key' => METRIC_EXECUTIONS_COMPUTE, - 'value' => ($executionsCompute['value'] * -1), - ]; - } - break; - default: - break; - } - } catch (\Exception $e) { - console::error("[reducer] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}"); - } catch (\Throwable $e) { - } finally { - $pools->reclaim(); - } - } -} diff --git a/src/Appwrite/Platform/Workers/UsageHook.php b/src/Appwrite/Platform/Workers/UsageHook.php deleted file mode 100644 index e5a535d9a5..0000000000 --- a/src/Appwrite/Platform/Workers/UsageHook.php +++ /dev/null @@ -1,105 +0,0 @@ -setType(Action::TYPE_WORKER_START) - ->inject('register') - ->inject('cache') - ->inject('pools') - ->callback(function ($register, $cache, $pools) { - $this->action($register, $cache, $pools); - }) - ; - } - - /** - * @param $register - * @param $cache - * @param $pools - * @return void - */ - public function action($register, $cache, $pools): void - { - Timer::tick(30000, function () use ($register, $cache, $pools) { - - $offset = count(self::$stats); - $projects = array_slice(self::$stats, 0, $offset, true); - array_splice(self::$stats, 0, $offset); - - foreach ($projects as $projectInternalId => $project) { - try { - $dbForProject = new Database( - $pools - ->get($project['database']) - ->pop() - ->getResource(), - $cache - ); - - $dbForProject->setNamespace('_' . $projectInternalId); - - foreach ($project['keys'] ?? [] as $key => $value) { - if ($value == 0) { - continue; - } - - foreach ($this->periods as $period => $format) { - $time = 'inf' === $period ? null : date($format, time()); - $id = \md5("{$time}_{$period}_{$key}"); - - try { - $dbForProject->createDocument('stats', new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $key, - 'value' => $value, - 'region' => App::getEnv('_APP_REGION', 'default'), - ])); - } catch (Duplicate $th) { - if ($value < 0) { - $dbForProject->decreaseDocumentAttribute( - 'stats', - $id, - 'value', - abs($value) - ); - } else { - $dbForProject->increaseDocumentAttribute( - 'stats', - $id, - 'value', - $value - ); - } - } - } - } - } catch (\Exception $e) { - console::error("[logger] " . " {DateTime::now()} " . " {$projectInternalId} " . " {$e->getMessage()}"); - } finally { - $pools->reclaim(); - } - } - }); - } -} diff --git a/src/Appwrite/Usage/Calculator.php b/src/Appwrite/Usage/Calculator.php new file mode 100644 index 0000000000..37c130a34a --- /dev/null +++ b/src/Appwrite/Usage/Calculator.php @@ -0,0 +1,15 @@ +region = $region; + } + + abstract public function collect(): void; +} diff --git a/src/Appwrite/Usage/Calculators/TimeSeries.php b/src/Appwrite/Usage/Calculators/TimeSeries.php new file mode 100644 index 0000000000..e0a12b443f --- /dev/null +++ b/src/Appwrite/Usage/Calculators/TimeSeries.php @@ -0,0 +1,557 @@ + '1h', + 'startTime' => '-24 hours' + ], + [ + 'key' => '1d', + 'startTime' => '-30 days' + ] + ]; + + /** + * All the metrics that we are collecting + * + * @var array + */ + protected array $metrics = [ + 'project.$all.network.requests' => [ + 'table' => 'appwrite_usage_project_{scope}_network_requests', + ], + 'project.$all.network.bandwidth' => [ + 'table' => 'appwrite_usage_project_{scope}_network_bandwidth', + ], + 'project.$all.network.inbound' => [ + 'table' => 'appwrite_usage_project_{scope}_network_inbound', + ], + 'project.$all.network.outbound' => [ + 'table' => 'appwrite_usage_project_{scope}_network_outbound', + ], + /* Users service metrics */ + 'users.$all.requests.create' => [ + 'table' => 'appwrite_usage_users_{scope}_requests_create', + ], + 'users.$all.requests.read' => [ + 'table' => 'appwrite_usage_users_{scope}_requests_read', + ], + 'users.$all.requests.update' => [ + 'table' => 'appwrite_usage_users_{scope}_requests_update', + ], + 'users.$all.requests.delete' => [ + 'table' => 'appwrite_usage_users_{scope}_requests_delete', + ], + + 'databases.$all.requests.create' => [ + 'table' => 'appwrite_usage_databases_{scope}_requests_create', + ], + 'databases.$all.requests.read' => [ + 'table' => 'appwrite_usage_databases_{scope}_requests_read', + ], + 'databases.$all.requests.update' => [ + 'table' => 'appwrite_usage_databases_{scope}_requests_update', + ], + 'databases.$all.requests.delete' => [ + 'table' => 'appwrite_usage_databases_{scope}_requests_delete', + ], + + 'collections.$all.requests.create' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_create', + ], + 'collections.$all.requests.read' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_read', + ], + 'collections.$all.requests.update' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_update', + ], + 'collections.$all.requests.delete' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_delete', + ], + + 'documents.$all.requests.create' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_create', + ], + 'documents.$all.requests.read' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_read', + ], + 'documents.$all.requests.update' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_update', + ], + 'documents.$all.requests.delete' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_delete', + ], + + 'collections.databaseId.requests.create' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_create', + 'groupBy' => ['databaseId'], + ], + 'collections.databaseId.requests.read' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_read', + 'groupBy' => ['databaseId'], + ], + 'collections.databaseId.requests.update' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_update', + 'groupBy' => ['databaseId'], + ], + 'collections.databaseId.requests.delete' => [ + 'table' => 'appwrite_usage_collections_{scope}_requests_delete', + 'groupBy' => ['databaseId'], + ], + + 'documents.databaseId.requests.create' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_create', + 'groupBy' => ['databaseId'], + ], + 'documents.databaseId.requests.read' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_read', + 'groupBy' => ['databaseId'], + ], + 'documents.databaseId.requests.update' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_update', + 'groupBy' => ['databaseId'], + ], + 'documents.databaseId.requests.delete' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_delete', + 'groupBy' => ['databaseId'], + ], + + 'documents.databaseId/collectionId.requests.create' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_create', + 'groupBy' => ['databaseId', 'collectionId'], + ], + 'documents.databaseId/collectionId.requests.read' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_read', + 'groupBy' => ['databaseId', 'collectionId'], + ], + 'documents.databaseId/collectionId.requests.update' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_update', + 'groupBy' => ['databaseId', 'collectionId'], + ], + 'documents.databaseId/collectionId.requests.delete' => [ + 'table' => 'appwrite_usage_documents_{scope}_requests_delete', + 'groupBy' => ['databaseId', 'collectionId'], + ], + + 'buckets.$all.requests.create' => [ + 'table' => 'appwrite_usage_buckets_{scope}_requests_create', + ], + 'buckets.$all.requests.read' => [ + 'table' => 'appwrite_usage_buckets_{scope}_requests_read', + ], + 'buckets.$all.requests.update' => [ + 'table' => 'appwrite_usage_buckets_{scope}_requests_update', + ], + 'buckets.$all.requests.delete' => [ + 'table' => 'appwrite_usage_buckets_{scope}_requests_delete', + ], + + 'files.$all.requests.create' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_create', + ], + 'files.$all.requests.read' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_read', + ], + 'files.$all.requests.update' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_update', + ], + 'files.$all.requests.delete' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_delete', + ], + + 'files.bucketId.requests.create' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_create', + 'groupBy' => ['bucketId'], + ], + 'files.bucketId.requests.read' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_read', + 'groupBy' => ['bucketId'], + ], + 'files.bucketId.requests.update' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_update', + 'groupBy' => ['bucketId'], + ], + 'files.bucketId.requests.delete' => [ + 'table' => 'appwrite_usage_files_{scope}_requests_delete', + 'groupBy' => ['bucketId'], + ], + + 'sessions.$all.requests.create' => [ + 'table' => 'appwrite_usage_sessions__{scope}_requests_create', + ], + 'sessions.provider.requests.create' => [ + 'table' => 'appwrite_usage_sessions_{scope}_requests_create', + 'groupBy' => ['provider'], + ], + 'sessions.$all.requests.delete' => [ + 'table' => 'appwrite_usage_sessions_{scope}_requests_delete', + ], + 'executions.$all.compute.total' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute', + ], + 'builds.$all.compute.total' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute', + ], + 'executions.$all.compute.failure' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute', + 'filters' => [ + 'functionStatus' => 'failed', + ], + ], + 'builds.$all.compute.failure' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute', + 'filters' => [ + 'functionStatus' => 'failed', + ], + ], + 'executions.$all.compute.success' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute', + 'filters' => [ + 'functionStatus' => 'success', + ], + ], + 'builds.$all.compute.success' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute', + 'filters' => [ + 'functionStatus' => 'success', + ], + ], + 'executions.functionId.compute.total' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute', + 'groupBy' => ['functionId'], + ], + 'builds.functionId.compute.total' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute', + 'groupBy' => ['functionId'], + ], + + 'executions.functionId.compute.failure' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute', + 'groupBy' => ['functionId'], + 'filters' => [ + 'functionStatus' => 'failed', + ], + ], + 'builds.functionId.compute.failure' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute', + 'groupBy' => ['functionId'], + 'filters' => [ + 'functionBuildStatus' => 'failed', + ], + ], + 'executions.functionId.compute.success' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute', + 'groupBy' => ['functionId'], + 'filters' => [ + 'functionStatus' => 'success', + ], + ], + 'builds.functionId.compute.success' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute', + 'groupBy' => ['functionId'], + 'filters' => [ + 'functionBuildStatus' => 'success', + ], + ], + + // counters + 'users.$all.count.total' => [ + 'table' => 'appwrite_usage_users_{scope}_count_total', + ], + 'buckets.$all.count.total' => [ + 'table' => 'appwrite_usage_buckets_{scope}_count_total', + ], + 'files.$all.count.total' => [ + 'table' => 'appwrite_usage_files_{scope}_count_total', + ], + 'files.bucketId.count.total' => [ + 'table' => 'appwrite_usage_files_{scope}_count_total', + 'groupBy' => ['bucketId'] + ], + 'databases.$all.count.total' => [ + 'table' => 'appwrite_usage_databases_{scope}_count_total', + ], + 'collections.$all.count.total' => [ + 'table' => 'appwrite_usage_collections_{scope}_count_total', + ], + 'documents.$all.count.total' => [ + 'table' => 'appwrite_usage_documents_{scope}_count_total', + ], + 'collections.databaseId.count.total' => [ + 'table' => 'appwrite_usage_collections_{scope}_count_total', + 'groupBy' => ['databaseId'] + ], + 'documents.databaseId.count.total' => [ + 'table' => 'appwrite_usage_documents_{scope}_count_total', + 'groupBy' => ['databaseId'] + ], + 'documents.databaseId/collectionId.count.total' => [ + 'table' => 'appwrite_usage_documents_{scope}_count_total', + 'groupBy' => ['databaseId', 'collectionId'] + ], + 'deployments.$all.storage.size' => [ + 'table' => 'appwrite_usage_deployments_{scope}_storage_size', + ], + 'project.$all.storage.size' => [ + 'table' => 'appwrite_usage_project_{scope}_storage_size', + ], + 'files.$all.storage.size' => [ + 'table' => 'appwrite_usage_files_{scope}_storage_size', + ], + 'files.$bucketId.storage.size' => [ + 'table' => 'appwrite_usage_files_{scope}_storage_size', + 'groupBy' => ['bucketId'] + ], + + 'builds.$all.compute.time' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute_time', + ], + 'executions.$all.compute.time' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute_time', + ], + + 'executions.functionId.compute.time' => [ + 'table' => 'appwrite_usage_executions_{scope}_compute_time', + 'groupBy' => ['functionId'], + ], + 'builds.functionId.compute.time' => [ + 'table' => 'appwrite_usage_builds_{scope}_compute_time', + 'groupBy' => ['functionId'], + ], + + 'project.$all.compute.time' => [ // Built time + execution time + 'table' => 'appwrite_usage_project_{scope}_compute_time', + 'groupBy' => ['functionId'], + ], + + 'deployments.$all.storage.size' => [ + 'table' => 'appwrite_usage_deployments_{scope}_storage_size' + ], + 'project.$all.storage.size' => [ + 'table' => 'appwrite_usage_project_{scope}_storage_size' + ], + 'files.$all.storage.size' => [ + 'table' => 'appwrite_usage_files_{scope}_storage_size' + ], + 'files.bucketId.storage.size' => [ + 'table' => 'appwrite_usage_files_{scope}_storage_size', + 'groupBy' => ['bucketId'] + ] + ]; + + public function __construct(string $region, Database $database, InfluxDatabase $influxDB, callable $getProjectDB, Registry $register, callable $errorHandler = null) + { + parent::__construct($region); + $this->database = $database; + $this->influxDB = $influxDB; + $this->getProjectDB = $getProjectDB; + $this->register = $register; + $this->errorHandler = $errorHandler; + } + + /** + * Create or Update Mertic + * Create or update each metric in the stats collection for the given project + * + * @param string $projectId + * @param int $time + * @param string $period + * @param string $metric + * @param int $value + * @param int $type + * + * @return void + */ + private function createOrUpdateMetric(string $projectId, string $time, string $period, string $metric, int $value, int $type): void + { + $id = \md5("{$time}_{$period}_{$metric}"); + $project = $this->database->getDocument('projects', $projectId); + $database = call_user_func($this->getProjectDB, $project); + + try { + $document = $database->getDocument('stats', $id); + if ($document->isEmpty()) { + $database->createDocument('stats', new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $metric, + 'value' => $value, + 'type' => $type, + 'region' => $this->region, + ])); + } else { + $database->updateDocument( + 'stats', + $document->getId(), + $document->setAttribute('value', $value) + ); + } + } catch (\Exception $e) { // if projects are deleted this might fail + if (is_callable($this->errorHandler)) { + call_user_func($this->errorHandler, $e, "sync_project_{$projectId}_metric_{$metric}"); + } else { + throw $e; + } + } + + $this->register->get('pools')->reclaim(); + } + + /** + * Sync From InfluxDB + * Sync stats from influxDB to stats collection in the Appwrite database + * + * @param string $metric + * @param array $options + * @param array $period + * + * @return void + */ + private function syncFromInfluxDB(string $metric, array $options, array $period): void + { + $start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339); + if (!empty($this->latestTime[$metric][$period['key']])) { + $start = $this->latestTime[$metric][$period['key']]; + } + $end = (new DateTime())->format(DateTime::RFC3339); + + $table = $options['table']; //Which influxdb table to query for this metric + $groupBy = empty($options['groupBy']) ? '' : ', ' . implode(', ', array_map(fn($groupBy) => '"' . $groupBy . '" ', $options['groupBy'])); //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc + + $filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status + if (!empty($filters)) { + $filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters))); + } else { + $filters = ''; + } + + $query = "SELECT sum(value) AS \"value\" "; + $query .= "FROM \"{$table}\" "; + $query .= "WHERE \"time\" > '{$start}' "; + $query .= "AND \"time\" < '{$end}' "; + $query .= "AND \"metric_type\"='counter' {$filters} "; + $query .= "GROUP BY time({$period['key']}), \"projectId\" {$groupBy} "; + $query .= "FILL(null)"; + + try { + $result = $this->influxDB->query($query); + $points = $result->getPoints(); + foreach ($points as $point) { + $projectId = $point['projectId']; + + if (!empty($projectId) && $projectId !== 'console') { + $metricUpdated = $metric; + if (!empty($groupBy)) { + foreach ($options['groupBy'] as $groupBy) { + $groupedBy = $point[$groupBy] ?? ''; + if (empty($groupedBy)) { + continue; + } + $metricUpdated = str_replace($groupBy, $groupedBy, $metricUpdated); + } + } + + $value = (!empty($point['value'])) ? $point['value'] : 0; + + $this->createOrUpdateMetric( + $point['projectId'], + $point['time'], + $period['key'], + $metricUpdated, + $value, + 0 + ); + $this->latestTime[$metric][$period['key']] = $point['time']; + } + } + } catch (\Exception $e) { // if projects are deleted this might fail + if (is_callable($this->errorHandler)) { + call_user_func($this->errorHandler, $e, "sync_metric_{$metric}_influxdb"); + } else { + throw $e; + } + } + } + + /** + * Collect Stats + * Collect all the stats from Influd DB to Database + * + * @return void + */ + public function collect(): void + { + foreach ($this->periods as $period) { + foreach ($this->metrics as $metric => $options) { //for each metrics + try { + $this->syncFromInfluxDB($metric, $options, $period); + } catch (\Exception $e) { + if (is_callable($this->errorHandler)) { + call_user_func($this->errorHandler, $e); + } else { + throw $e; + } + } + } + } + } +} diff --git a/src/Appwrite/Usage/Stats.php b/src/Appwrite/Usage/Stats.php new file mode 100644 index 0000000000..e6e0056664 --- /dev/null +++ b/src/Appwrite/Usage/Stats.php @@ -0,0 +1,225 @@ +statsd = $statsd; + } + + /** + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function setParam(string $key, $value): self + { + $this->params[$key] = $value; + + return $this; + } + + /** + * @param string $key + * + * @return mixed|null + */ + public function getParam(string $key) + { + return (isset($this->params[$key])) ? $this->params[$key] : null; + } + + /** + * @param string $namespace + * + * @return $this + */ + public function setNamespace(string $namespace): self + { + $this->namespace = $namespace; + + return $this; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Submit data to StatsD. + * Send various metrics to StatsD based on the parameters that are set + * @return void + */ + public function submit(): void + { + $projectId = $this->params['projectId'] ?? ''; + $projectInternalId = $this->params['projectInternalId']; + $tags = ",projectInternalId={$projectInternalId},projectId={$projectId},version=" . App::getEnv('_APP_VERSION', 'UNKNOWN'); + + // the global namespace is prepended to every key (optional) + $this->statsd->setNamespace($this->namespace); + + $httpRequest = $this->params['project.{scope}.network.requests'] ?? 0; + $httpMethod = $this->params['httpMethod'] ?? ''; + if ($httpRequest >= 1) { + $this->statsd->increment('project.{scope}.network.requests' . $tags . ',method=' . \strtolower($httpMethod)); + } + + $inbound = $this->params['project.{scope}.network.inbound'] ?? 0; + $outbound = $this->params['project.{scope}.network.outbound'] ?? 0; + $this->statsd->count('project.{scope}.network.inbound' . $tags, $inbound); + $this->statsd->count('project.{scope}.network.outbound' . $tags, $outbound); + $this->statsd->count('project.{scope}.network.bandwidth' . $tags, $inbound + $outbound); + + $usersMetrics = [ + 'users.{scope}.requests.create', + 'users.{scope}.requests.read', + 'users.{scope}.requests.update', + 'users.{scope}.requests.delete', + 'users.{scope}.count.total', + ]; + + foreach ($usersMetrics as $metric) { + $value = $this->params[$metric] ?? 0; + if ($value === 1 || $value === -1) { + $this->statsd->count($metric . $tags, $value); + } + } + + $dbMetrics = [ + 'databases.{scope}.requests.create', + 'databases.{scope}.requests.read', + 'databases.{scope}.requests.update', + 'databases.{scope}.requests.delete', + 'collections.{scope}.requests.create', + 'collections.{scope}.requests.read', + 'collections.{scope}.requests.update', + 'collections.{scope}.requests.delete', + 'documents.{scope}.requests.create', + 'documents.{scope}.requests.read', + 'documents.{scope}.requests.update', + 'documents.{scope}.requests.delete', + 'databases.{scope}.count.total', + 'collections.{scope}.count.total', + 'documents.{scope}.count.total' + ]; + + foreach ($dbMetrics as $metric) { + $value = $this->params[$metric] ?? 0; + if ($value === 1 || $value === -1) { + $dbTags = $tags . ",collectionId=" . ($this->params['collectionId'] ?? '') . ",databaseId=" . ($this->params['databaseId'] ?? ''); + $this->statsd->count($metric . $dbTags, $value); + } + } + + $storageMertics = [ + 'buckets.{scope}.requests.create', + 'buckets.{scope}.requests.read', + 'buckets.{scope}.requests.update', + 'buckets.{scope}.requests.delete', + 'files.{scope}.requests.create', + 'files.{scope}.requests.read', + 'files.{scope}.requests.update', + 'files.{scope}.requests.delete', + 'buckets.{scope}.count.total', + 'files.{scope}.count.total', + 'files.{scope}.storage.size' + ]; + + foreach ($storageMertics as $metric) { + $value = $this->params[$metric] ?? 0; + if ($value !== 0) { + $storageTags = $tags . ",bucketId=" . ($this->params['bucketId'] ?? ''); + $this->statsd->count($metric . $storageTags, $value); + } + } + + $sessionsMetrics = [ + 'sessions.{scope}.requests.create', + 'sessions.{scope}.requests.update', + 'sessions.{scope}.requests.delete', + ]; + + foreach ($sessionsMetrics as $metric) { + $value = $this->params[$metric] ?? 0; + if ($value >= 1) { + $sessionTags = $tags . ",provider=" . ($this->params['provider'] ?? ''); + $this->statsd->count($metric . $sessionTags, $value); + } + } + + $functionId = $this->params['functionId'] ?? ''; + $functionExecution = $this->params['executions.{scope}.compute'] ?? 0; + $functionExecutionTime = ($this->params['executionTime'] ?? 0) * 1000; // ms + $functionExecutionStatus = $this->params['executionStatus'] ?? ''; + + $functionBuild = $this->params['builds.{scope}.compute'] ?? 0; + $functionBuildTime = ($this->params['buildTime'] ?? 0) * 1000; // ms + $functionBuildStatus = $this->params['buildStatus'] ?? ''; + $functionCompute = $functionExecutionTime + $functionBuildTime; + $functionTags = $tags . ',functionId=' . $functionId; + + $deploymentSize = $this->params['deployment.{scope}.storage.size'] ?? 0; + $storageSize = $this->params['files.{scope}.storage.size'] ?? 0; + if ($deploymentSize + $storageSize > 0 || $deploymentSize + $storageSize <= -1) { + $this->statsd->count('project.{scope}.storage.size' . $tags, $deploymentSize + $storageSize); + } + + if ($deploymentSize !== 0) { + $this->statsd->count('deployments.{scope}.storage.size' . $functionTags, $deploymentSize); + } + + if ($functionExecution >= 1) { + $this->statsd->increment('executions.{scope}.compute' . $functionTags . ',functionStatus=' . $functionExecutionStatus); + if ($functionExecutionTime > 0) { + $this->statsd->count('executions.{scope}.compute.time' . $functionTags, $functionExecutionTime); + } + } + if ($functionBuild >= 1) { + $this->statsd->increment('builds.{scope}.compute' . $functionTags . ',functionBuildStatus=' . $functionBuildStatus); + $this->statsd->count('builds.{scope}.compute.time' . $functionTags, $functionBuildTime); + } + if ($functionBuild + $functionExecution >= 1) { + $this->statsd->count('project.{scope}.compute.time' . $functionTags, $functionCompute); + } + + $this->reset(); + } + + public function reset(): self + { + $this->params = []; + $this->namespace = 'appwrite.usage'; + + return $this; + } +} diff --git a/src/Appwrite/Utopia/Response/Model/Migration.php b/src/Appwrite/Utopia/Response/Model/Migration.php index 78e8658032..5a54eef3ad 100644 --- a/src/Appwrite/Utopia/Response/Model/Migration.php +++ b/src/Appwrite/Utopia/Response/Model/Migration.php @@ -68,6 +68,7 @@ class Migration extends Model ->addRule('errors', [ 'type' => self::TYPE_STRING, 'description' => 'All errors that occurred during the migration process.', + 'array' => true, 'default' => [], 'example' => [], ]) diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php index 83b8744760..8c6c81f234 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -16,7 +16,7 @@ class UsageBuckets extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('filesTotal', [ + ->addRule('filesCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of files in this bucket.', 'default' => [], @@ -30,6 +30,34 @@ class UsageBuckets extends Model 'example' => [], 'array' => true ]) + ->addRule('filesCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageCollection.php b/src/Appwrite/Utopia/Response/Model/UsageCollection.php index 5abcf46b7d..8b6966fcd2 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageCollection.php +++ b/src/Appwrite/Utopia/Response/Model/UsageCollection.php @@ -16,13 +16,41 @@ class UsageCollection extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('documentsTotal', [ + ->addRule('documentsCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], 'example' => [], 'array' => true ]) + ->addRule('documentsCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index 58d49c506e..0c84d796ba 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -16,16 +16,72 @@ class UsageDatabase extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('collectionsTotal', [ + ->addRule('documentsCount', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for total number of documents.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of collections.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documentsTotal', [ + ->addRule('documentsCreate', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of documents.', + 'description' => 'Aggregated stats for documents created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections delete.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php index a6008ca9e6..93488a47db 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php @@ -16,23 +16,107 @@ class UsageDatabases extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('databasesTotal', [ + ->addRule('databasesCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('collectionsTotal', [ + ->addRule('documentsCount', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for total number of documents.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of collections.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documentsTotal', [ + ->addRule('databasesCreate', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of documents.', + 'description' => 'Aggregated stats for documents created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('databasesRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('databasesUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('databasesDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for total number of collections.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('documentsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for documents deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('collectionsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for collections delete.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageFunction.php b/src/Appwrite/Utopia/Response/Model/UsageFunction.php index 03acaa750a..58d76bbf41 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageFunction.php +++ b/src/Appwrite/Utopia/Response/Model/UsageFunction.php @@ -16,16 +16,30 @@ class UsageFunction extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('deploymentsTotal', [ + ->addRule('executionsTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of function deployments.', + 'description' => 'Aggregated stats for number of function executions.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('deploymentsStorage', [ + ->addRule('executionsFailure', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function deployments storage.', + 'description' => 'Aggregated stats for function execution failures.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('executionsSuccess', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function execution successes.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('executionsTime', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function execution duration.', 'default' => [], 'example' => [], 'array' => true @@ -37,31 +51,23 @@ class UsageFunction extends Model 'example' => [], 'array' => true ]) - ->addRule('buildsStorage', [ + ->addRule('buildsFailure', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for builds storage.', + 'description' => 'Aggregated stats for function build failures.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('buildsSuccess', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function build successes.', 'default' => [], 'example' => [], 'array' => true ]) ->addRule('buildsTime', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build compute.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('executionsTotal', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of function executions.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - - ->addRule('executionsTime', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution compute.', + 'description' => 'Aggregated stats for function build duration.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageFunctions.php b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php index 6ab36e21ac..7adb0d4aa3 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageFunctions.php +++ b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php @@ -16,23 +16,30 @@ class UsageFunctions extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('functionsTotal', [ + ->addRule('executionsTotal', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of functions.', + 'description' => 'Aggregated stats for number of function executions.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('deploymentsTotal', [ + ->addRule('executionsFailure', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of function deployments.', + 'description' => 'Aggregated stats for function execution failures.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('deploymentsStorage', [ + ->addRule('executionsSuccess', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function deployments storage.', + 'description' => 'Aggregated stats for function execution successes.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('executionsTime', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function execution duration.', 'default' => [], 'example' => [], 'array' => true @@ -44,31 +51,23 @@ class UsageFunctions extends Model 'example' => [], 'array' => true ]) - ->addRule('buildsStorage', [ + ->addRule('buildsFailure', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for builds storage.', + 'description' => 'Aggregated stats for function build failures.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('buildsSuccess', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for function build successes.', 'default' => [], 'example' => [], 'array' => true ]) ->addRule('buildsTime', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function build compute.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - ->addRule('executionsTotal', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for number of function executions.', - 'default' => [], - 'example' => [], - 'array' => true - ]) - - ->addRule('executionsTime', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for function execution compute.', + 'description' => 'Aggregated stats for function build duration.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 641613809a..e37bc5928d 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -16,7 +16,7 @@ class UsageProject extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('requestsTotal', [ + ->addRule('requests', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of requests.', 'default' => [], @@ -30,42 +30,42 @@ class UsageProject extends Model 'example' => [], 'array' => true ]) - ->addRule('executionsTotal', [ + ->addRule('executions', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for function executions.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('documentsTotal', [ + ->addRule('documents', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of documents.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('databasesTotal', [ + ->addRule('databases', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of databases.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('usersTotal', [ + ->addRule('users', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of users.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('filesStorage', [ + ->addRule('storage', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('bucketsTotal', [ + ->addRule('buckets', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for number of buckets.', 'default' => [], diff --git a/src/Appwrite/Utopia/Response/Model/UsageStorage.php b/src/Appwrite/Utopia/Response/Model/UsageStorage.php index 88d0beca01..7e3c08e12a 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageStorage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageStorage.php @@ -16,23 +16,79 @@ class UsageStorage extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('bucketsTotal', [ + ->addRule('storage', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for total number of buckets.', + 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('filesTotal', [ + ->addRule('filesCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of files.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('filesStorage', [ + ->addRule('bucketsCount', [ 'type' => Response::MODEL_METRIC, - 'description' => 'Aggregated stats for the occupied storage size (in bytes).', + 'description' => 'Aggregated stats for total number of buckets.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('bucketsCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for buckets created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('bucketsRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for buckets read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('bucketsUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for buckets updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('bucketsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for buckets deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('filesDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for files deleted.', 'default' => [], 'example' => [], 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageUsers.php b/src/Appwrite/Utopia/Response/Model/UsageUsers.php index c0cc4baa54..4c7b37d50f 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageUsers.php +++ b/src/Appwrite/Utopia/Response/Model/UsageUsers.php @@ -16,21 +16,62 @@ class UsageUsers extends Model 'default' => '', 'example' => '30d', ]) - ->addRule('usersTotal', [ + ->addRule('usersCount', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for total number of users.', 'default' => [], 'example' => [], 'array' => true ]) - - ->addRule('sessionsTotal', [ + ->addRule('usersCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for users created.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('usersRead', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for users read.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('usersUpdate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for users updated.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('usersDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for users deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('sessionsCreate', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated stats for sessions created.', 'default' => [], 'example' => [], 'array' => true ]) + ->addRule('sessionsProviderCreate', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('sessionsDelete', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated stats for sessions deleted.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 36f6ad0dc5..c7388069a1 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -70,7 +70,7 @@ class Executor array $variables = [], string $command = null, ) { - $runtimeId = "$projectId-$deploymentId"; + $runtimeId = "$projectId-$deploymentId-build"; $route = "/runtimes"; $params = [ 'runtimeId' => $runtimeId, @@ -113,7 +113,7 @@ class Executor ) { $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); - $runtimeId = "$projectId-$deploymentId"; + $runtimeId = "$projectId-$deploymentId-build"; $route = "/runtimes/{$runtimeId}/logs"; $params = [ 'timeout' => $timeout @@ -177,6 +177,7 @@ class Executor string $method, array $headers, string $runtimeEntrypoint = null, + int $requestTimeout = null ) { if (empty($headers['host'])) { $headers['host'] = App::getEnv('_APP_DOMAIN', ''); @@ -202,9 +203,13 @@ class Executor 'runtimeEntrypoint' => $runtimeEntrypoint, ]; - $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); + // Safety timeout. Executor has timeout, and open runtime has soft timeout. + // This one shouldn't really happen, but prevents from unexpected networking behaviours. + if ($requestTimeout == null) { + $requestTimeout = $timeout + 15; + } - $response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); + $response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $requestTimeout); $status = $response['headers']['status-code']; if ($status >= 400) { diff --git a/tests/e2e/Services/Avatars/AvatarsBase.php b/tests/e2e/Services/Avatars/AvatarsBase.php index b8581b952e..ba66920ed6 100644 --- a/tests/e2e/Services/Avatars/AvatarsBase.php +++ b/tests/e2e/Services/Avatars/AvatarsBase.php @@ -202,7 +202,7 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/image', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io/images/apple.png', + 'url' => 'https://appwrite.io/images/open-graph/website.png', ]); $this->assertEquals(200, $response['headers']['status-code']); diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index f51da53cb1..ec46e30768 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -193,44 +193,6 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals($response['body'], ""); } - /** - * @depends testCreateCollection - */ - public function testGetDatabaseUsage(array $data) - { - $databaseId = $data['databaseId']; - /** - * Test for FAILURE - */ - - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '32h' - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - - /** - * Test for SUCCESS - */ - - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h' - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(count($response['body']), 3); - $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['documentsTotal']); - $this->assertIsArray($response['body']['collectionsTotal']); - } - - /** * @depends testCreateCollection */ @@ -268,10 +230,15 @@ class DatabasesConsoleClientTest extends Scope ], $this->getHeaders()), [ 'range' => '24h' ]); + $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(count($response['body']), 2); + $this->assertEquals(count($response['body']), 6); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['documentsTotal']); + $this->assertIsArray($response['body']['documentsCount']); + $this->assertIsArray($response['body']['documentsCreate']); + $this->assertIsArray($response['body']['documentsRead']); + $this->assertIsArray($response['body']['documentsUpdate']); + $this->assertIsArray($response['body']['documentsDelete']); } /** diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index 053f886032..743df9e53a 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -80,6 +80,7 @@ class DatabasesCustomClientTest extends Scope Permission::write(Role::user($this->getUser()['$id'])), ] ]); + $this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index 3dad6cb386..b458a4316b 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -92,15 +92,16 @@ class FunctionsConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 8); + $this->assertEquals(count($response['body']), 9); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['deploymentsTotal']); - $this->assertIsArray($response['body']['deploymentsStorage']); - $this->assertIsArray($response['body']['buildsTotal']); - $this->assertIsArray($response['body']['buildsStorage']); - $this->assertIsArray($response['body']['buildsTime']); $this->assertIsArray($response['body']['executionsTotal']); + $this->assertIsArray($response['body']['executionsFailure']); + $this->assertIsArray($response['body']['executionsSuccess']); $this->assertIsArray($response['body']['executionsTime']); + $this->assertIsArray($response['body']['buildsTotal']); + $this->assertIsArray($response['body']['buildsFailure']); + $this->assertIsArray($response['body']['buildsSuccess']); + $this->assertIsArray($response['body']['buildsTime']); } /** diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 60b7f7542e..cdc9ec846f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -32,8 +32,8 @@ class FunctionsCustomServerTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [ - 'users.*.create', - 'users.*.delete', + 'buckets.*.create', + 'buckets.*.delete', ], 'schedule' => '0 0 1 1 *', 'timeout' => 10, @@ -50,8 +50,8 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, $dateValidator->isValid($response1['body']['$updatedAt'])); $this->assertEquals('', $response1['body']['deployment']); $this->assertEquals([ - 'users.*.create', - 'users.*.delete', + 'buckets.*.create', + 'buckets.*.delete', ], $response1['body']['events']); $this->assertEquals('0 0 1 1 *', $response1['body']['schedule']); $this->assertEquals(10, $response1['body']['timeout']); @@ -191,8 +191,8 @@ class FunctionsCustomServerTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [ - 'users.*.create', - 'users.*.delete', + 'buckets.*.create', + 'buckets.*.delete', ], 'schedule' => '0 0 1 1 *', 'timeout' => 10, @@ -1231,4 +1231,117 @@ class FunctionsCustomServerTest extends Scope $this->assertArrayHasKey('base', $runtime); $this->assertArrayHasKey('supports', $runtime); } + + + public function testEventTrigger() + { + $timeout = 5; + $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-event/code.tar.gz"; + $this->packageCode('php-event'); + + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'functionId' => ID::unique(), + 'name' => 'Test PHP Event executions', + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'events' => [ + 'users.*.create', + ], + 'timeout' => $timeout, + ]); + + $functionId = $function['body']['$id'] ?? ''; + + $this->assertEquals(201, $function['headers']['status-code']); + + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'entrypoint' => 'index.php', + 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'activate' => true + ]); + + $deploymentId = $deployment['body']['$id'] ?? ''; + $this->assertEquals(202, $deployment['headers']['status-code']); + + // Poll until deployment is built + while (true) { + $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + if ( + $deployment['headers']['status-code'] >= 400 + || \in_array($deployment['body']['status'], ['ready', 'failed']) + ) { + break; + } + + \sleep(1); + } + + $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $deployment['headers']['status-code']); + + // Wait a little for activation to finish + sleep(5); + + // Create user to trigger event + $user = $this->client->call(Client::METHOD_POST, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'userId' => 'unique()', + 'name' => 'Event User' + ]); + + $userId = $user['body']['$id']; + + $this->assertEquals(201, $user['headers']['status-code']); + + // Wait for execution to occur + sleep(15); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $execution = $executions['body']['executions'][0]; + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $execution['status']); + $this->assertEquals(204, $execution['responseStatusCode']); + $this->assertStringContainsString($userId, $execution['logs']); + $this->assertStringContainsString('Event User', $execution['logs']); + + // Cleanup : Delete function + $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], []); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Cleanup : Delete user + $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], []); + + $this->assertEquals(204, $response['headers']['status-code']); + } } diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index cfb9061df3..b95e3b251f 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -155,7 +155,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(4959, \strlen($initials['body'])); + $this->assertEquals(5041, \strlen($initials['body'])); return $initials['body']; } diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 96c9bde5c7..4ac15bd95c 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -29,10 +29,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['ping']); $this->assertLessThan(100, $response['body']['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -51,10 +47,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -73,10 +65,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -95,10 +83,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -117,10 +101,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['statuses'][0]['ping']); $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -141,10 +121,6 @@ class HealthCustomServerTest extends Scope $this->assertNotEmpty($response['body']['localTime']); $this->assertLessThan(10, $response['body']['diff']); - /** - * Test for FAILURE - */ - return []; } @@ -162,10 +138,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); - /** - * Test for FAILURE - */ - return []; } @@ -183,10 +155,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); - /** - * Test for FAILURE - */ - return []; } @@ -204,9 +172,124 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['size']); $this->assertLessThan(100, $response['body']['size']); + return []; + } + + public function testFunctionsSuccess(): array + { /** - * Test for FAILURE + * Test for SUCCESS */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testBuildsSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/builds', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testDatabasesSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testDeletesSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/deletes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testMailsSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/mails', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testMessagingSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/messaging', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + return []; + } + + public function testMigrationsSuccess(): array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/migrations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); return []; } @@ -226,10 +309,6 @@ class HealthCustomServerTest extends Scope $this->assertIsInt($response['body']['ping']); $this->assertLessThan(100, $response['body']['ping']); - /** - * Test for FAILURE - */ - return []; } @@ -248,10 +327,6 @@ class HealthCustomServerTest extends Scope $this->assertIsString($response['body']['status']); $this->assertIsString($response['body']['version']); - /** - * Test for FAILURE - */ - return []; } } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 826c90cef8..ff3f8e8e94 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -445,7 +445,7 @@ class ProjectsConsoleClientTest extends Scope /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/project/usage', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -454,14 +454,14 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(count($response['body']), 9); $this->assertNotEmpty($response['body']); $this->assertEquals('30d', $response['body']['range']); - $this->assertIsArray($response['body']['requestsTotal']); + $this->assertIsArray($response['body']['requests']); $this->assertIsArray($response['body']['network']); - $this->assertIsArray($response['body']['executionsTotal']); - $this->assertIsArray($response['body']['documentsTotal']); - $this->assertIsArray($response['body']['databasesTotal']); - $this->assertIsArray($response['body']['bucketsTotal']); - $this->assertIsArray($response['body']['usersTotal']); - $this->assertIsArray($response['body']['filesStorage']); + $this->assertIsArray($response['body']['executions']); + $this->assertIsArray($response['body']['documents']); + $this->assertIsArray($response['body']['databases']); + $this->assertIsArray($response['body']['buckets']); + $this->assertIsArray($response['body']['users']); + $this->assertIsArray($response['body']['storage']); /** * Test for FAILURE diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index aff055f18d..8fda8e0464 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -39,11 +39,10 @@ class StorageConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(4, count($response['body'])); + $this->assertEquals(12, count($response['body'])); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['bucketsTotal']); - $this->assertIsArray($response['body']['filesTotal']); - $this->assertIsArray($response['body']['filesStorage']); + $this->assertIsArray($response['body']['storage']); + $this->assertIsArray($response['body']['filesCount']); } public function testGetStorageBucketUsage() @@ -95,9 +94,13 @@ class StorageConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 3); + $this->assertEquals(count($response['body']), 7); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['filesTotal']); + $this->assertIsArray($response['body']['filesCount']); + $this->assertIsArray($response['body']['filesCreate']); + $this->assertIsArray($response['body']['filesRead']); + $this->assertIsArray($response['body']['filesUpdate']); + $this->assertIsArray($response['body']['filesDelete']); $this->assertIsArray($response['body']['filesStorage']); } } diff --git a/tests/e2e/Services/Teams/TeamsBaseServer.php b/tests/e2e/Services/Teams/TeamsBaseServer.php index 9db17abaae..7bb6241c4a 100644 --- a/tests/e2e/Services/Teams/TeamsBaseServer.php +++ b/tests/e2e/Services/Teams/TeamsBaseServer.php @@ -234,7 +234,7 @@ trait TeamsBaseServer /** * @depends testUpdateMembershipRoles */ - public function testDeleteUserUpdatesTeamMembershipCount($data): void + public function testDeleteUserUpdatesTeamMembershipCount($data) { $teamUid = $data['teamUid'] ?? ''; $userUid = $data['userUid'] ?? ''; diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index baf601789a..3327bb7558 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -595,6 +595,18 @@ trait UsersBase $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => '>', + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertEmpty($response['body']['users']); + $this->assertCount(0, $response['body']['users']); + /** * Test for FAILURE */ diff --git a/tests/e2e/Services/Users/UsersConsoleClientTest.php b/tests/e2e/Services/Users/UsersConsoleClientTest.php index 8943bfab63..9cc23e825a 100644 --- a/tests/e2e/Services/Users/UsersConsoleClientTest.php +++ b/tests/e2e/Services/Users/UsersConsoleClientTest.php @@ -23,6 +23,17 @@ class UsersConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ 'range' => '32h', + 'provider' => 'email' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h', + 'provider' => 'some-random-provider' ]); $this->assertEquals($response['headers']['status-code'], 400); @@ -35,12 +46,38 @@ class UsersConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ 'range' => '24h', + 'provider' => 'email' ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 3); + $this->assertEquals(count($response['body']), 9); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['usersTotal']); - $this->assertIsArray($response['body']['sessionsTotal']); + $this->assertIsArray($response['body']['usersCount']); + $this->assertIsArray($response['body']['usersCreate']); + $this->assertIsArray($response['body']['usersRead']); + $this->assertIsArray($response['body']['usersUpdate']); + $this->assertIsArray($response['body']['usersDelete']); + $this->assertIsArray($response['body']['sessionsCreate']); + $this->assertIsArray($response['body']['sessionsProviderCreate']); + $this->assertIsArray($response['body']['sessionsDelete']); + + $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 9); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['usersCount']); + $this->assertIsArray($response['body']['usersCreate']); + $this->assertIsArray($response['body']['usersRead']); + $this->assertIsArray($response['body']['usersUpdate']); + $this->assertIsArray($response['body']['usersDelete']); + $this->assertIsArray($response['body']['sessionsCreate']); + $this->assertIsArray($response['body']['sessionsProviderCreate']); + $this->assertIsArray($response['body']['sessionsDelete']); } } diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 3baae7316d..bdb9bf49a4 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -66,7 +66,9 @@ services: environment: - _APP_ENV - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_FUNCTIONS diff --git a/tests/unit/Usage/StatsTest.php b/tests/unit/Usage/StatsTest.php index 794caebdf5..f021841396 100644 --- a/tests/unit/Usage/StatsTest.php +++ b/tests/unit/Usage/StatsTest.php @@ -2,53 +2,70 @@ namespace Tests\Unit\Usage; -use Appwrite\URL\URL as AppwriteURL; +use Appwrite\Usage\Stats; use PHPUnit\Framework\TestCase; use Utopia\App; -use Utopia\DSN\DSN; -use Utopia\Queue; -use Utopia\Queue\Client; -use Utopia\Queue\Connection; class StatsTest extends TestCase { - protected ?Connection $connection = null; - protected ?Client $client = null; - - protected const QUEUE_NAME = 'usage-test-q'; + /** + * @var Stats + */ + protected $object = null; public function setUp(): void { - $env = App::getEnv('_APP_CONNECTIONS_QUEUE', AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => App::getEnv('_APP_REDIS_USER', ''), - 'pass' => App::getEnv('_APP_REDIS_PASS', ''), - ])); + $host = App::getEnv('_APP_STATSD_HOST', 'telegraf'); + $port = App::getEnv('_APP_STATSD_PORT', 8125); - $dsn = explode('=', $env); - $dsn = count($dsn) > 1 ? $dsn[1] : $dsn[0]; - $dsn = new DSN($dsn); - $this->connection = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); - $this->client = new Client(self::QUEUE_NAME, $this->connection); + $connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port); + $statsd = new \Domnikl\Statsd\Client($connection); + + $this->object = new Stats($statsd); } public function tearDown(): void { } - public function testSamePayload(): void + public function testNamespace(): void { - $inToQueue = [ - 'key_1' => 'value_1', - 'key_2' => 'value_2', - ]; + $this->object->setNamespace('appwritetest.usage'); + $this->assertEquals('appwritetest.usage', $this->object->getNamespace()); + } - $result = $this->client->enqueue($inToQueue); - $this->assertTrue($result); - $outFromQueue = $this->connection->leftPopArray('utopia-queue.queue.' . self::QUEUE_NAME, 0)['payload']; - $this->assertNotEmpty($outFromQueue); - $this->assertSame($inToQueue, $outFromQueue); + public function testParams(): void + { + $this->object + ->setParam('projectId', 'appwrite_test') + ->setParam('projectInternalId', 1) + ->setParam('networkRequestSize', 100) + ; + + $this->assertEquals('appwrite_test', $this->object->getParam('projectId')); + $this->assertEquals(1, $this->object->getParam('projectInternalId')); + $this->assertEquals(100, $this->object->getParam('networkRequestSize')); + + $this->object->submit(); + + $this->assertEquals(null, $this->object->getParam('projectId')); + $this->assertEquals(null, $this->object->getParam('networkRequestSize')); + } + + public function testReset(): void + { + $this->object + ->setParam('projectId', 'appwrite_test') + ->setParam('networkRequestSize', 100) + ; + + $this->assertEquals('appwrite_test', $this->object->getParam('projectId')); + $this->assertEquals(100, $this->object->getParam('networkRequestSize')); + + $this->object->reset(); + + $this->assertEquals(null, $this->object->getParam('projectId')); + $this->assertEquals(null, $this->object->getParam('networkRequestSize')); + $this->assertEquals('appwrite.usage', $this->object->getNamespace()); } } From 0c776e0a4496c6ac55163e830b9089cee5bb9593 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 11:04:44 +0300 Subject: [PATCH 092/144] comparing $workersCount via worker.php vs init php --- .env | 2 -- app/worker.php | 11 +++++++++-- docker-compose.yml | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.env b/.env index 89f5980a44..feeb74d849 100644 --- a/.env +++ b/.env @@ -1,8 +1,6 @@ _APP_ENV=development _APP_LOCALE=en _APP_WORKER_PER_CORE=6 -_APP_WORKERS_NUM=1 -_APP_QUEUE_NAME=database_db_main _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/app/worker.php b/app/worker.php index 6995c2664e..1fb1c14b25 100644 --- a/app/worker.php +++ b/app/worker.php @@ -221,10 +221,17 @@ if (!isset($args[1])) { $workerName = $args[0]; $workerIndex = $args[1] ?? ''; -if (!empty($workerNum)) { +if (!empty($workerIndex)) { $workerName .= '_' . $workerIndex; } +$multiprocessing = App::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; +if ($multiprocessing) { + $workerCount = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); +} else { + $workerCount = 1; +} + try { /** * Any worker can be configured with the following env vars: @@ -233,7 +240,7 @@ try { * - _APP_QUEUE_NAME The name of the queue to read for database events */ $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => App::getEnv('_APP_WORKERS_NUM', swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6))), + 'workerCount' => $workerCount, 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, 'queueName' => App::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)) diff --git a/docker-compose.yml b/docker-compose.yml index f29b90d360..2d3541d800 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -389,8 +389,8 @@ services: - _APP_DB_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG - - _APP_WORKERS_NUM - - _APP_QUEUE_NAME + - _APP_SERVER_MULTIPROCESS=disabled + - _APP_QUEUE_NAME=database_db_main appwrite-worker-builds: entrypoint: worker-builds From 0e2c1c1f2d0fe498147fa9aa4d163280560f2630 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 16:20:41 +0300 Subject: [PATCH 093/144] small fixes --- .env | 2 +- app/controllers/api/teams.php | 2 +- app/worker.php | 11 ++--------- docker-compose.yml | 5 +++-- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.env b/.env index feeb74d849..fb4cb658bf 100644 --- a/.env +++ b/.env @@ -98,4 +98,4 @@ _APP_VCS_GITHUB_CLIENT_SECRET= _APP_VCS_GITHUB_WEBHOOK_SECRET= _APP_MIGRATIONS_FIREBASE_CLIENT_ID= _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET= -_APP_ASSISTANT_OPENAI_API_KEY= \ No newline at end of file +_APP_ASSISTANT_OPENAI_API_KEY= diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index a32f79e44b..4a0378105f 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -388,7 +388,7 @@ App::post('/v1/teams/:teamId/memberships') ->inject('queueForMails') ->inject('queueForMessaging') ->inject('queueForEvents') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) { + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, EventPhone $queueForMessaging, Event $queueForEvents) { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); diff --git a/app/worker.php b/app/worker.php index 1fb1c14b25..6995c2664e 100644 --- a/app/worker.php +++ b/app/worker.php @@ -221,17 +221,10 @@ if (!isset($args[1])) { $workerName = $args[0]; $workerIndex = $args[1] ?? ''; -if (!empty($workerIndex)) { +if (!empty($workerNum)) { $workerName .= '_' . $workerIndex; } -$multiprocessing = App::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; -if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); -} else { - $workerCount = 1; -} - try { /** * Any worker can be configured with the following env vars: @@ -240,7 +233,7 @@ try { * - _APP_QUEUE_NAME The name of the queue to read for database events */ $platform->init(Service::TYPE_WORKER, [ - 'workerCount' => $workerCount, + 'workersNum' => App::getEnv('_APP_WORKERS_NUM', swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6))), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, 'queueName' => App::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)) diff --git a/docker-compose.yml b/docker-compose.yml index 2d3541d800..38340205f2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -321,6 +321,7 @@ services: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - ./vendor/utopia-php/storage:/usr/src/code/vendor/utopia-php/storage + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform environment: - _APP_ENV - _APP_WORKER_PER_CORE @@ -371,6 +372,7 @@ services: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database + - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform depends_on: - redis - mariadb @@ -388,8 +390,7 @@ services: - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_PROVIDER - - _APP_LOGGING_CONFIG - - _APP_SERVER_MULTIPROCESS=disabled + - _APP_WORKERS_NUM=1 - _APP_QUEUE_NAME=database_db_main appwrite-worker-builds: From e660a6f4a3acd69191ebc291bbb7ca1317d03ec2 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 17:09:51 +0300 Subject: [PATCH 094/144] small fixes --- app/console | 1 - 1 file changed, 1 deletion(-) delete mode 160000 app/console diff --git a/app/console b/app/console deleted file mode 160000 index 0f9e5e7839..0000000000 --- a/app/console +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0f9e5e78396dcfe8b2a02131afe874bd84ba4085 From 5dc55110b4533b3a2fc1cebc43132790bd446428 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 17:34:55 +0300 Subject: [PATCH 095/144] small fixes --- composer.lock | 6039 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6039 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..cb581fc578 --- /dev/null +++ b/composer.lock @@ -0,0 +1,6039 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "fd17c629205269db6d1f0b3288a9769e", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/appwrite", + "version": "10.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-for-php.git", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\": "src/Appwrite" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", + "support": { + "email": "team@appwrite.io", + "issues": "https://github.com/appwrite/sdk-for-php/issues", + "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", + "url": "https://appwrite.io/support" + }, + "time": "2023-09-07T23:28:31+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "f3897169f5c1f365312238a516ae9465f804634f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", + "reference": "f3897169f5c1f365312238a516ae9465f804634f", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" + }, + "time": "2023-02-24T09:50:42+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.13.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "5ab496b3908992b39275994a23783701c4b3de84" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.7.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2023-09-12T19:38:43+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1.4", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-25T09:12:45+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:20:53+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-08-03T15:11:55+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:13:57+00:00" + }, + { + "name": "influxdb/influxdb-php", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/influxdata/influxdb-php.git", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": "^5.5 || ^7.0 || ^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "Curl extension, needed for Curl driver", + "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB\\": "src/InfluxDB" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Hoogendijk", + "email": "stephen@tca0.nl" + }, + { + "name": "Daniel Martinez", + "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" + } + ], + "description": "InfluxDB client library for PHP", + "keywords": [ + "client", + "influxdata", + "influxdb", + "influxdb class", + "influxdb client", + "influxdb library", + "time series" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-php/issues", + "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" + }, + "abandoned": true, + "time": "2020-12-26T17:45:17+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "league/csv", + "version": "9.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.0", + "phpstan/phpstan-phpunit": "^0.12.0", + "phpstan/phpstan-strict-rules": "^0.12.0", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2021-04-17T16:32:08+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.1.6", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "5cbea85106e561c7138d03603eb6e05128480409" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", + "reference": "5cbea85106e561c7138d03603eb6e05128480409", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2023-10-02T10:01:54+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.11.0", + "jean85/pretty-package-versions": "^1.2 || ^2.0.1", + "php": "^7.1 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "squizlabs/php_codesniffer": "^3.6", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + }, + "time": "2021-10-20T22:22:37+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2023-03-06T14:43:22+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.31.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.31.1" + }, + "time": "2023-08-29T11:07:46+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.33.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.33.1" + }, + "time": "2023-08-29T11:07:40+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.43.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.3.*" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.4.*", + "pcov/clobber": "^2.0", + "phpstan/phpstan": "1.10.*", + "phpunit/phpunit": "^9.4", + "rregeer/phpunit-coverage-check": "^0.3.1", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistence using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.43.5" + }, + "time": "2023-10-06T06:49:47+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/0.3.2" + }, + "time": "2023-07-19T16:39:24+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.31.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.31.0" + }, + "time": "2023-08-30T16:10:04+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + }, + "time": "2023-09-14T20:48:42+00:00" + }, + { + "name": "utopia-php/migration", + "version": "0.3.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/migration.git", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "shasum": "" + }, + "require": { + "appwrite/appwrite": "10.0.*", + "php": "8.*", + "utopia-php/cli": "0.*" + }, + "require-dev": { + "laravel/pint": "1.*", + "phpunit/phpunit": "9.*", + "vlucas/phpdotenv": "5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Migration\\": "src/Migration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to migrate resources between services.", + "keywords": [ + "framework", + "migration", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/0.3.5" + }, + "time": "2023-09-25T16:51:47+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.10.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.3.1" + }, + "time": "2023-09-01T17:25:28+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "dev-integrate-workers", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "e657da8874cbf0a5e2837357be3a4d37486d37ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/e657da8874cbf0a5e2837357be3a4d37486d37ea", + "reference": "e657da8874cbf0a5e2837357be3a4d37486d37ea", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "framework", + "php", + "platform", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/integrate-workers" + }, + "time": "2023-10-16T13:08:24+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "dev-feat-get-worker-start", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/5f81095e0ec66b2902037181ba3a20f6f9541af7", + "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" + }, + "time": "2023-10-04T14:49:13+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.17.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-xz": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.17.0" + }, + "time": "2023-08-21T11:28:36+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.7.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.7.1" + }, + "time": "2023-08-30T09:14:37+00:00" + }, + { + "name": "utopia-php/vcs", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/vcs.git", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "shasum": "" + }, + "require": { + "adhocore/jwt": "^1.1", + "php": ">=8.0", + "utopia-php/cache": "^0.8.0", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\VCS\\": "src/VCS", + "Utopia\\Detector\\": "src/Detector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", + "keywords": [ + "framework", + "php", + "utopia", + "vcs" + ], + "support": { + "issues": "https://github.com/utopia-php/vcs/issues", + "source": "https://github.com/utopia-php/vcs/tree/0.5.0" + }, + "time": "2023-09-13T19:05:52+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.10", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-07-05T14:23:37+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.35.2", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" + }, + "time": "2023-09-14T14:59:50+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.71", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2023-04-25T20:33:03+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "bcad8d995980440892759db0c32acae7c8e79442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + }, + "time": "2023-09-26T12:28:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-19T04:57:46+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/5.0.2" + }, + "time": "2023-03-20T06:05:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-08-28T11:09:02+00:00" + } + ], + "aliases": [ + { + "package": "utopia-php/platform", + "version": "dev-integrate-workers", + "alias": "0.3.3", + "alias_normalized": "0.3.3.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-get-worker-start", + "alias": "0.5.3", + "alias_normalized": "0.5.3.0" + } + ], + "minimum-stability": "stable", + "stability-flags": { + "utopia-php/platform": 20, + "utopia-php/queue": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From 039df4abb385e5a744ae2e11b283b3274a11f4c5 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 18:04:27 +0300 Subject: [PATCH 096/144] revert changes --- .../Services/Databases/DatabasesConsoleClientTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index ec46e30768..04ac00c263 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -263,7 +263,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'queries' => ['offset(1)', 'limit(1)'], + 'limit' => 1 ]); $this->assertEquals(200, $logs['headers']['status-code']); @@ -275,8 +275,8 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'queries' => ['offset(1)'] - ]); + 'offset' => 1 + ]); $this->assertEquals(200, $logs['headers']['status-code']); $this->assertIsArray($logs['body']['logs']); @@ -286,7 +286,8 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'queries' => ['offset(1)', 'limit(1)'], + 'offset' => 1, + 'limit' => 1 ]); $this->assertEquals(200, $logs['headers']['status-code']); From 710ac707e35a8118f7b58687ff7825979b94c3fc Mon Sep 17 00:00:00 2001 From: Shimon Newman Date: Mon, 16 Oct 2023 18:11:29 +0300 Subject: [PATCH 097/144] Update src/Appwrite/Event/Audit.php Co-authored-by: Christy Jacob --- src/Appwrite/Event/Audit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index 2321fe77dd..f36ec78790 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -18,7 +18,7 @@ class Audit extends Event $this ->setQueue(Event::AUDITS_QUEUE_NAME) - ->setClass(Event::BUILDS_CLASS_NAME); + ->setClass(Event::AUDITS_CLASS_NAME); } From a8f5886474b5900cc4cc33c053b8a299caeb2841 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 18:17:02 +0300 Subject: [PATCH 098/144] minor fix --- .gitmodules | 2 +- app/views/install/compose.phtml | 2 ++ docker-compose.yml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 5506e01d6f..8ec131892e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.2 + branch = 3.2.1 diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 8928107029..11fb5dae61 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -345,6 +345,8 @@ services: - _APP_DB_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG + - _APP_WORKERS_NUM=1 + - _APP_QUEUE_NAME=database_db_main appwrite-worker-builds: image: /: diff --git a/docker-compose.yml b/docker-compose.yml index 38340205f2..d463390941 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -390,6 +390,7 @@ services: - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG - _APP_WORKERS_NUM=1 - _APP_QUEUE_NAME=database_db_main From 0e2cf14e127304a3d282df33a96c4318422aa52a Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 16 Oct 2023 18:17:13 +0300 Subject: [PATCH 099/144] minor fix --- src/Appwrite/Event/Audit.php | 1 - tests/unit/Event/EventTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index f36ec78790..4b02849970 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -19,7 +19,6 @@ class Audit extends Event $this ->setQueue(Event::AUDITS_QUEUE_NAME) ->setClass(Event::AUDITS_CLASS_NAME); - } /** diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index b48fc8f849..a430a7fdc6 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -15,7 +15,6 @@ require_once __DIR__ . '/../../../app/init.php'; class EventTest extends TestCase { - protected ?Event $object = null; protected string $queue = ''; From e565557eb2a697069bff95054da0b64e817f250c Mon Sep 17 00:00:00 2001 From: Shimon Newman Date: Mon, 16 Oct 2023 20:13:39 +0300 Subject: [PATCH 100/144] Update app/worker.php Co-authored-by: Christy Jacob --- app/worker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/worker.php b/app/worker.php index 6995c2664e..c9e830ffeb 100644 --- a/app/worker.php +++ b/app/worker.php @@ -256,7 +256,7 @@ $worker ->inject('error') ->inject('logger') ->inject('log') - ->action(function (Throwable $error, Logger|null $logger, Log $log) { + ->action(function (Throwable $error, ?Logger $logger, Log $log) { $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); if ($error instanceof PDOException) { From dbfe5e45aab8dff8e7086a3fd7e57a38ededdf21 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Oct 2023 12:18:51 +1300 Subject: [PATCH 101/144] Delegate custom deletes --- app/controllers/api/account.php | 2 +- app/controllers/api/databases.php | 36 +-- app/controllers/api/functions.php | 2 - app/controllers/api/teams.php | 2 +- app/controllers/api/users.php | 2 +- app/init.php | 3 + src/Appwrite/Event/Audit.php | 1 - src/Appwrite/Platform/Workers/Databases.php | 293 ++++++++++++++------ tests/unit/Event/EventTest.php | 1 - 9 files changed, 235 insertions(+), 107 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 93df84a6f6..d6aaaa24bb 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1110,7 +1110,7 @@ App::put('/v1/account/sessions/magic-url') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Request $request, Response $response,Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + ->action(function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { /** @var Utopia\Database\Document $user */ diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index b4709e656e..cdfdd8172b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -674,9 +674,9 @@ App::delete('/v1/databases/:databaseId') ->param('databaseId', '', new UID(), 'Database ID.') ->inject('response') ->inject('dbForProject') + ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('queueForDeletes') - ->action(function (string $databaseId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { + ->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -691,9 +691,9 @@ App::delete('/v1/databases/:databaseId') $dbForProject->deleteCachedDocument('databases', $database->getId()); $dbForProject->deleteCachedCollection('databases_' . $database->getInternalId()); - $queueForDeletes - ->setType(DELETE_TYPE_DOCUMENT) - ->setDocument($database); + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_DATABASE) + ->setDatabase($database); $queueForEvents ->setParam('databaseId', $database->getId()) @@ -1040,10 +1040,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->inject('mode') + ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('queueForDeletes') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Delete $queueForDeletes) { + ->inject('mode') + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); @@ -1063,9 +1063,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - $queueForDeletes - ->setType(DELETE_TYPE_DOCUMENT) - ->setDocument($collection); + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_COLLECTION) + ->setDatabase($database) + ->setCollection($collection); $queueForEvents ->setContext('database', $database) @@ -2291,8 +2292,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->setType(DATABASE_TYPE_DELETE_ATTRIBUTE) ->setCollection($collection) ->setDatabase($db) - ->setDocument($attribute) - ; + ->setDocument($attribute); // Select response model based on type and format $type = $attribute->getAttribute('type'); @@ -3458,10 +3458,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') + ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('queueForDeletes') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -3532,8 +3532,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $processDocument($collection, $document); - $queueForDeletes - ->setType(DELETE_TYPE_AUDIT) + $queueForDatabase + ->setType(DATABASE_TYPE_DELETE_DOCUMENT) + ->setDatabase($database) + ->setCollection($collection) ->setDocument($document); $queueForEvents diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 383adebe67..3a1c96690e 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1680,8 +1680,6 @@ App::post('/v1/functions/:functionId/executions') $execution->setAttribute('logs', $executionResponse['logs']); $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); - - } catch (\Throwable $th) { $durationEnd = \microtime(true); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 99fcfeb38c..4a0378105f 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -257,7 +257,7 @@ App::put('/v1/teams/:teamId') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp , Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $name, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index bd709ee272..a992d51965 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -130,7 +130,7 @@ App::post('/v1/users') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response,Document $project, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) { $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $queueForEvents); $response diff --git a/app/init.php b/app/init.php index be152f830b..6d0f52e94e 100644 --- a/app/init.php +++ b/app/init.php @@ -147,6 +147,9 @@ const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute'; const DATABASE_TYPE_CREATE_INDEX = 'createIndex'; const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute'; const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex'; +const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection'; +const DATABASE_TYPE_DELETE_DOCUMENT = 'deleteDocument'; +const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase'; // Build Worker Types const BUILD_TYPE_DEPLOYMENT = 'deployment'; const BUILD_TYPE_RETRY = 'retry'; diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php index 2321fe77dd..75b4ef573d 100644 --- a/src/Appwrite/Event/Audit.php +++ b/src/Appwrite/Event/Audit.php @@ -19,7 +19,6 @@ class Audit extends Event $this ->setQueue(Event::AUDITS_QUEUE_NAME) ->setClass(Event::BUILDS_CLASS_NAME); - } /** diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 788924a238..9a2caf9c4e 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -4,14 +4,18 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Event; use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Utopia\Response\Model\Platform; use Exception; +use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Conflict; +use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Exception as DatabaseException; +use Utopia\Database\Query; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -56,19 +60,18 @@ class Databases extends Action $document = new Document($payload['document'] ?? []); $database = new Document($payload['database'] ?? []); - if ($collection->isEmpty()) { - throw new Exception('Missing collection'); - } - - if ($document->isEmpty()) { - throw new Exception('Missing document'); + if ($database->isEmpty()) { + throw new Exception('Missing database'); } match (strval($type)) { + DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $project, $dbForProject), + DATABASE_TYPE_DELETE_COLLECTION => $this->deleteCollection($database, $collection, $project, $dbForProject), DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForConsole, $dbForProject), DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForConsole, $dbForProject), DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForConsole, $dbForProject), DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForConsole, $dbForProject), + DATABASE_TYPE_DELETE_DOCUMENT => $this->deleteDocument($database, $collection, $document, $project, $dbForProject), default => Console::error('No database operation for type: ' . $type), }; } @@ -87,6 +90,12 @@ class Databases extends Action */ private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void { + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + if ($attribute->isEmpty()) { + throw new Exception('Missing attribute'); + } $projectId = $project->getId(); @@ -173,25 +182,7 @@ class Databases extends Action ); } } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project, - ); - - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); + $this->trigger($database, $collection, $attribute, $project, $projectId, $events); } if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { @@ -215,6 +206,13 @@ class Databases extends Action **/ private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void { + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + if ($attribute->isEmpty()) { + throw new Exception('Missing attribute'); + } + $projectId = $project->getId(); $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ @@ -284,25 +282,7 @@ class Databases extends Action ); } } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); + $this->trigger($database, $collection, $attribute, $project, $projectId, $events); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -380,6 +360,13 @@ class Databases extends Action */ private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void { + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + if ($index->isEmpty()) { + throw new Exception('Missing index'); + } + $projectId = $project->getId(); $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ @@ -412,25 +399,7 @@ class Databases extends Action $index->setAttribute('status', 'failed') ); } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); + $this->trigger($database, $collection, $index, $project, $projectId, $events); } $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId); @@ -451,6 +420,13 @@ class Databases extends Action */ private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForConsole, Database $dbForProject): void { + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + if ($index->isEmpty()) { + throw new Exception('Missing index'); + } + $projectId = $project->getId(); $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ @@ -470,7 +446,6 @@ class Databases extends Action } catch (\Exception $e) { Console::error($e->getMessage()); - if ($e instanceof DatabaseException) { $index->setAttribute('error', $e->getMessage()); } @@ -480,27 +455,179 @@ class Databases extends Action $index->setAttribute('status', 'stuck') ); } finally { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $index, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $index->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); + $this->trigger($database, $collection, $index, $project, $projectId, $events); } $dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } + + /** + * @param Document $database + * @param Document $project + * @param $dbForProject + * @return void + * @throws Exception + */ + protected function deleteDatabase(Document $database, Document $project, $dbForProject): void + { + $this->deleteByGroup('database_' . $database->getInternalId(), [], $dbForProject, function ($collection) use ($database, $project, $dbForProject) { + $this->deleteCollection($database, $collection, $project, $dbForProject); + }); + + $dbForProject->deleteCollection('database_' . $database->getInternalId()); + + $this->deleteAuditLogsByResource('database/' . $database->getId(), $project, $dbForProject); + } + + /** + * @param Document $database + * @param Document $collection + * @param Document $project + * @param Database $dbForProject + * @return void + * @throws Authorization + * @throws Conflict + * @throws DatabaseException + * @throws Restricted + * @throws Structure + */ + protected function deleteCollection(Document $database, Document $collection, Document $project, Database $dbForProject): void + { + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + + $collectionId = $collection->getId(); + $collectionInternalId = $collection->getInternalId(); + $databaseId = $database->getId(); + $databaseInternalId = $database->getInternalId(); + + $relationships = \array_filter( + $collection->getAttribute('attributes'), + fn ($attribute) => $attribute['type'] === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + if (!$relationship['twoWay']) { + continue; + } + $relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']); + $dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']); + $dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId()); + $dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId()); + } + + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); + + $this->deleteByGroup('attributes', [ + Query::equal('databaseInternalId', [$databaseInternalId]), + Query::equal('collectionInternalId', [$collectionInternalId]) + ], $dbForProject); + + $this->deleteByGroup('indexes', [ + Query::equal('databaseInternalId', [$databaseInternalId]), + Query::equal('collectionInternalId', [$collectionInternalId]) + ], $dbForProject); + + $this->deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project, $dbForProject); + } + + private function deleteDocument(Document $database, Document $collection, Document $document, Document $project, Database $dbForProject) + { + if ($collection->isEmpty()) { + throw new Exception('Missing collection'); + } + if ($document->isEmpty()) { + throw new Exception('Missing document'); + } + + $this->deleteAuditLogsByResource('document/' . $document->getId(), $project, $dbForProject); + } + + /** + * @param string $resource + * @param Document $project + * @param Database $dbForProject + * @return void + * @throws Exception + */ + protected function deleteAuditLogsByResource(string $resource, Document $project, Database $dbForProject): void + { + $this->deleteByGroup(Audit::COLLECTION, [ + Query::equal('resource', [$resource]) + ], $dbForProject); + } + + /** + * @param string $collection collectionID + * @param array $queries + * @param Database $database + * @param callable|null $callback + * @return void + * @throws Exception + */ + protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void + { + $count = 0; + $chunk = 0; + $limit = 50; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + + $sum = count($results); + + Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); + + foreach ($results as $document) { + if ($database->deleteDocument($document->getCollection(), $document->getId())) { + Console::success('Deleted document "' . $document->getId() . '" successfully'); + + if (\is_callable($callback)) { + $callback($document); + } + } else { + Console::error('Failed to delete document: ' . $document->getId()); + } + $count++; + } + } + + $executionEnd = \microtime(true); + + Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + } + + protected function trigger( + Document $database, + Document $collection, + Document $attribute, + Document $project, + string $projectId, + array $events + ): void { + $target = Realtime::fromPayload( + // Pass first, most verbose event pattern + event: $events[0], + payload: $attribute, + project: $project, + ); + Realtime::send( + projectId: 'console', + payload: $attribute->getArrayCopy(), + events: $events, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'projectId' => $projectId, + 'databaseId' => $database->getId(), + 'collectionId' => $collection->getId() + ] + ); + } } diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index b48fc8f849..a430a7fdc6 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -15,7 +15,6 @@ require_once __DIR__ . '/../../../app/init.php'; class EventTest extends TestCase { - protected ?Event $object = null; protected string $queue = ''; From 9a6d88a819755bca1ec14b610801fdc164814c93 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 06:44:36 +0300 Subject: [PATCH 102/144] addressing some comments --- .env | 2 +- app/cli.php | 6 +-- app/worker.php | 44 +++---------------- composer.json | 4 +- docker-compose.yml | 18 +------- src/Appwrite/Event/Database.php | 2 +- src/Appwrite/Event/Migration.php | 18 -------- src/Appwrite/Platform/Tasks/Schedule.php | 6 +++ src/Appwrite/Platform/Workers/Builds.php | 16 +++---- .../Platform/Workers/Certificates.php | 8 +++- src/Appwrite/Platform/Workers/Databases.php | 17 ++++--- src/Appwrite/Platform/Workers/Migrations.php | 10 ++--- 12 files changed, 45 insertions(+), 106 deletions(-) diff --git a/.env b/.env index fb4cb658bf..feeb74d849 100644 --- a/.env +++ b/.env @@ -98,4 +98,4 @@ _APP_VCS_GITHUB_CLIENT_SECRET= _APP_VCS_GITHUB_WEBHOOK_SECRET= _APP_MIGRATIONS_FIREBASE_CLIENT_ID= _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET= -_APP_ASSISTANT_OPENAI_API_KEY= +_APP_ASSISTANT_OPENAI_API_KEY= \ No newline at end of file diff --git a/app/cli.php b/app/cli.php index 1903c81e69..a24e4c386e 100644 --- a/app/cli.php +++ b/app/cli.php @@ -24,8 +24,6 @@ use Utopia\Registry\Registry; Authorization::disable(); -global $register; - CLI::setResource('register', fn()=>$register); CLI::setResource('cache', function ($pools) { @@ -146,8 +144,8 @@ CLI::setResource('influxdb', function (Registry $register) { CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); -CLI::setResource('queueForFunctions', function (Group $pools) { - return new Func($pools->get('queue')->pop()->getResource()); +CLI::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); }, ['pools']); CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); diff --git a/app/worker.php b/app/worker.php index 6995c2664e..661a7ee80f 100644 --- a/app/worker.php +++ b/app/worker.php @@ -32,12 +32,10 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Pools\Group; use Utopia\Queue\Connection; -use Utopia\Storage\Device; Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -global $register; Server::setResource('register', fn () => $register); @@ -118,6 +116,10 @@ Server::setResource('cache', function (Registry $register) { return new Cache(new Sharding($adapters)); }, ['register']); +Server::setResource('log', fn() => new Log()); +Server::setResource('usage', function ($register) { + return new Stats($register->get('statsd')); +}, ['register']); Server::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); @@ -148,60 +150,30 @@ Server::setResource('queueForFunctions', function (Connection $queue) { Server::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); -Server::setResource('usage', function ($register) { - return new Stats($register->get('statsd')); -}, ['register']); Server::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); Server::setResource('logger', function (Registry $register) { return $register->get('logger'); }, ['register']); - Server::setResource('pools', function (Registry $register) { return $register->get('pools'); }, ['register']); - -Server::setResource('log', fn() => new Log()); - -/** - * Get Functions Storage Device - * @param string $projectId of the project - * @return Device - */ Server::setResource('getFunctionsDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); }; }); - -/** - * Get Files Storage Device - * @param string $projectId of the project - * @return Device - */ Server::setResource('getFilesDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); }; }); - -/** - * Get Builds Storage Device - * @param string $projectId of the project - * @return Device - */ Server::setResource('getBuildsDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); }; }); - -/** - * Get cache Device - * @param string $projectId of the project - * @return Device - */ Server::setResource('getCacheDevice', function () { return function (string $projectId) { return getDevice(APP_STORAGE_CACHE . '/app-' . $projectId); @@ -221,7 +193,7 @@ if (!isset($args[1])) { $workerName = $args[0]; $workerIndex = $args[1] ?? ''; -if (!empty($workerNum)) { +if (!empty($workerIndex)) { $workerName .= '_' . $workerIndex; } @@ -291,13 +263,9 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); -try { - $workerStart = $worker->getWorkerStart(); -} catch (\Throwable $error) { $worker->workerStart() ->action(function () use ($workerName) { Console::info("Worker $workerName started"); }); -} -$worker->start(); + $worker->start(); diff --git a/composer.json b/composer.json index 9f79a5f414..f7340a69ba 100644 --- a/composer.json +++ b/composer.json @@ -59,10 +59,10 @@ "utopia-php/messaging": "0.2.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", - "utopia-php/platform": "dev-integrate-workers as 0.3.3", + "utopia-php/platform": "0.5.*", "utopia-php/pools": "0.4.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "dev-feat-get-worker-start as 0.5.3", + "utopia-php/queue": "0.5.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.17.*", "utopia-php/swoole": "0.5.*", diff --git a/docker-compose.yml b/docker-compose.yml index d463390941..15d56fc184 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,9 +84,6 @@ services: - ./public:/usr/src/code/public - ./src:/usr/src/code/src - ./dev:/usr/local/dev - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform - - ./vendor/utopia-php/framework:/usr/src/code/vendor/utopia-php/framework - depends_on: - mariadb - redis @@ -252,10 +249,6 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform - - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue - - ./vendor/utopia-php/pools:/usr/src/code/vendor/utopia-php/pools - depends_on: - redis - mariadb @@ -285,7 +278,6 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform depends_on: - redis - mariadb @@ -320,8 +312,6 @@ services: - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./vendor/utopia-php/storage:/usr/src/code/vendor/utopia-php/storage - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform environment: - _APP_ENV - _APP_WORKER_PER_CORE @@ -370,9 +360,7 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./src:/usr/src/code/src - - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform + - ./src:/usr/src/code/srcrm depends_on: - redis - mariadb @@ -406,8 +394,6 @@ services: - appwrite-builds:/storage/builds:rw - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./vendor/utopia-php/platform:/usr/src/code/vendor/utopia-php/platform - - ./vendor/utopia-php/queue:/usr/src/code/vendor/utopia-php/queue depends_on: - redis - mariadb @@ -637,8 +623,6 @@ services: volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./vendor/utopia-php/pools:/usr/src/code/vendor/utopia-php/pools - depends_on: - redis environment: diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index d9cbb141b8..892838746b 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -18,6 +18,7 @@ class Database extends Event { parent::__construct($connection); + $this->setQueue($this->getProject()->getAttribute('database')); $this->setClass(Event::DATABASE_CLASS_NAME); } @@ -108,7 +109,6 @@ class Database extends Event */ public function trigger(): string|bool { - $this->setQueue($this->getProject()->getAttribute('database')); $client = new Client($this->queue, $this->connection); diff --git a/src/Appwrite/Event/Migration.php b/src/Appwrite/Event/Migration.php index 09fb6a8699..93126cb358 100644 --- a/src/Appwrite/Event/Migration.php +++ b/src/Appwrite/Event/Migration.php @@ -87,22 +87,4 @@ class Migration extends Event 'migration' => $this->migration ]); } - - /** - * Schedules the migration event and schedules it in the migrations worker queue. - * - * @param \DateTime|int $at - * @return void - * @throws \Resque_Exception - * @throws \ResqueScheduler_InvalidTimestampException - */ - public function schedule(DateTime|int $at): void - { - return; -// ResqueScheduler::enqueueAt($at, $this->queue, $this->class, [ -// 'project' => $this->project, -// 'user' => $this->user, -// 'migration' => $this->migration -// ]); - } } diff --git a/src/Appwrite/Platform/Tasks/Schedule.php b/src/Appwrite/Platform/Tasks/Schedule.php index a136ee62b1..a4afdc3bdb 100644 --- a/src/Appwrite/Platform/Tasks/Schedule.php +++ b/src/Appwrite/Platform/Tasks/Schedule.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Tasks; +use Appwrite\Extend\Exception; use Cron\CronExpression; use Swoole\Timer; use Utopia\App; @@ -26,6 +27,9 @@ class Schedule extends Action return 'schedule'; } + /** + * @throws Exception + */ public function __construct() { $this @@ -34,6 +38,8 @@ class Schedule extends Action ->inject('dbForConsole') ->inject('getProjectDB') ->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); + + throw new Exception('error on Schedule cli'); } /** diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 5a317d6ec6..f1ef0df602 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -50,9 +50,9 @@ class Builds extends Action ->inject('queueForFunctions') ->inject('usage') ->inject('cache') - ->inject('getProjectDB') + ->inject('dbForProject') ->inject('getFunctionsDevice') - ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Stats $usage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $getProjectDB, $getFunctionsDevice)); + ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Stats $usage, Cache $cache, Database $dbForProject, callable $getFunctionsDevice) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $getFunctionsDevice)); } /** @@ -62,12 +62,12 @@ class Builds extends Action * @param Func $queueForFunctions * @param Stats $usage * @param Cache $cache - * @param callable $getProjectDB + * @param Database $dbForProject * @param callable $getFunctionsDevice * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Stats $usage, Cache $cache, callable $getProjectDB, callable $getFunctionsDevice): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Stats $usage, Cache $cache, Database $dbForProject, callable $getFunctionsDevice): void { $payload = $message->getPayload() ?? []; @@ -86,7 +86,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($getFunctionsDevice, $queueForFunctions, $queueForEvents, $usage, $dbForConsole, $getProjectDB, $github, $project, $resource, $deployment, $template); + $this->buildDeployment($getFunctionsDevice, $queueForFunctions, $queueForEvents, $usage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template); break; default: @@ -100,7 +100,7 @@ class Builds extends Action * @param Event $queueForEvents * @param Stats $usage * @param Database $dbForConsole - * @param callable $getProjectDB + * @param Database $dbForProject * @param GitHub $github * @param Document $project * @param Document $function @@ -110,12 +110,10 @@ class Builds extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(callable $getFunctionsDevice, Func $queueForFunctions, Event $queueForEvents, Stats $usage, Database $dbForConsole, callable $getProjectDB, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void + protected function buildDeployment(callable $getFunctionsDevice, Func $queueForFunctions, Event $queueForEvents, Stats $usage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); - $dbForProject = $getProjectDB($project); - $function = $dbForProject->getDocument('functions', $function->getId()); if ($function->isEmpty()) { throw new Exception('Function not found', 404); diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 00b008e72d..3ecc845a96 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -136,7 +136,7 @@ class Certificates extends Action // Validate domain and DNS records. Skip if job is forced if (!$skipRenewCheck) { - $mainDomain = $this->getMainDomain($dbForConsole); + $mainDomain = $this->getMainDomain(); $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; $this->validateDomain($domain, $isMainDomain); } @@ -225,7 +225,7 @@ class Certificates extends Action * * @return null|string Returns main domain. If null, there is no main domain yet. */ - private function getMainDomain(Database $dbForConsole): ?string + private function getMainDomain(): ?string { $envDomain = App::getEnv('_APP_DOMAIN', ''); if (!empty($envDomain) && $envDomain !== 'localhost') { @@ -498,6 +498,10 @@ class Certificates extends Action /** Trigger Functions */ $queueForFunctions + ->setProject($project) + ->setEvent('rules.[ruleId].update') + ->setParam('ruleId', $rule->getId()) + ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) ->trigger(); /** Trigger realtime event */ diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 788924a238..8f62df3982 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Event; use Appwrite\Messaging\Adapter\Realtime; -use Exception; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -23,7 +22,7 @@ class Databases extends Action } /** - * @throws Exception + * @throws \Exception */ public function __construct() { @@ -40,14 +39,14 @@ class Databases extends Action * @param Database $dbForConsole * @param Database $dbForProject * @return void - * @throws Exception + * @throws \Exception */ public function action(Message $message, Database $dbForConsole, Database $dbForProject): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { - throw new Exception('Missing payload'); + throw new \Exception('Missing payload'); } $type = $payload['type']; @@ -57,11 +56,11 @@ class Databases extends Action $database = new Document($payload['database'] ?? []); if ($collection->isEmpty()) { - throw new Exception('Missing collection'); + throw new \Exception('Missing collection'); } if ($document->isEmpty()) { - throw new Exception('Missing document'); + throw new \Exception('Missing document'); } match (strval($type)) { @@ -83,7 +82,7 @@ class Databases extends Action * @return void * @throws Authorization * @throws Conflict - * @throws Exception + * @throws \Exception */ private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void { @@ -144,7 +143,7 @@ class Databases extends Action break; default: if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new Exception('Failed to create Attribute'); + throw new \Exception('Failed to create Attribute'); } } @@ -211,7 +210,7 @@ class Databases extends Action * @return void * @throws Authorization * @throws Conflict - * @throws Exception + * @throws \Exception **/ private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForConsole, Database $dbForProject): void { diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index da63292aa2..31b0df59a3 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -44,19 +44,19 @@ class Migrations extends Action $this ->desc('Migrations worker') ->inject('message') - ->inject('getProjectDB') + ->inject('dbForProject') ->inject('dbForConsole') - ->callback(fn(Message $message, callable $getProjectDB, Database $dbForConsole) => $this->action($message, $getProjectDB, $dbForConsole)); + ->callback(fn(Message $message, Database $dbForProject, Database $dbForConsole) => $this->action($message, $dbForProject, $dbForConsole)); } /** * @param Message $message - * @param callable $getProjectDB + * @param Database $dbForProject * @param Database $dbForConsole * @return void * @throws Exception */ - public function action(Message $message, callable $getProjectDB, Database $dbForConsole): void + public function action(Message $message, Database $dbForProject, Database $dbForConsole): void { $payload = $message->getPayload() ?? []; @@ -72,7 +72,7 @@ class Migrations extends Action return; } - $this->dbForProject = $getProjectDB($project); + $this->dbForProject = $dbForProject; $this->dbForConsole = $dbForConsole; /** From e8dbd043ecc47c2f12080ca42cee598c1b0b1b8c Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 07:02:54 +0300 Subject: [PATCH 103/144] addressing some comments --- src/Appwrite/Event/Database.php | 3 ++- src/Appwrite/Platform/Tasks/Schedule.php | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index 892838746b..eb22f8897f 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -18,7 +18,6 @@ class Database extends Event { parent::__construct($connection); - $this->setQueue($this->getProject()->getAttribute('database')); $this->setClass(Event::DATABASE_CLASS_NAME); } @@ -110,6 +109,8 @@ class Database extends Event public function trigger(): string|bool { + $this->setQueue($this->getProject()->getAttribute('database')); + $client = new Client($this->queue, $this->connection); return $client->enqueue([ diff --git a/src/Appwrite/Platform/Tasks/Schedule.php b/src/Appwrite/Platform/Tasks/Schedule.php index a4afdc3bdb..57c9d22c4f 100644 --- a/src/Appwrite/Platform/Tasks/Schedule.php +++ b/src/Appwrite/Platform/Tasks/Schedule.php @@ -38,8 +38,6 @@ class Schedule extends Action ->inject('dbForConsole') ->inject('getProjectDB') ->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); - - throw new Exception('error on Schedule cli'); } /** From 56f565bf309b610c15edec45d5e535af9c3f0895 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 07:23:33 +0300 Subject: [PATCH 104/144] addressing some comments --- src/Appwrite/Event/Event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 235cb11e05..07f496788f 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -9,7 +9,7 @@ use Utopia\Queue\Connection; class Event { - public const DATABASE_QUEUE_NAME = 'v1-databases'; + public const DATABASE_QUEUE_NAME = 'v1-database'; public const DATABASE_CLASS_NAME = 'DatabaseV1'; public const DELETE_QUEUE_NAME = 'v1-deletes'; From d7b7dfef25d8bea4f6c218bcb20e93ee04ff5f13 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 07:28:51 +0300 Subject: [PATCH 105/144] addressing some comments --- composer.lock | 6039 ------------------------------------------------- 1 file changed, 6039 deletions(-) delete mode 100644 composer.lock diff --git a/composer.lock b/composer.lock deleted file mode 100644 index cb581fc578..0000000000 --- a/composer.lock +++ /dev/null @@ -1,6039 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "fd17c629205269db6d1f0b3288a9769e", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/appwrite", - "version": "10.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\": "src/Appwrite" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", - "support": { - "email": "team@appwrite.io", - "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", - "url": "https://appwrite.io/support" - }, - "time": "2023-09-07T23:28:31+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "f3897169f5c1f365312238a516ae9465f804634f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", - "reference": "f3897169f5c1f365312238a516ae9465f804634f", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" - }, - "time": "2023-02-24T09:50:42+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.13.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "5ab496b3908992b39275994a23783701c4b3de84" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.7.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2023-09-12T19:38:43+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1.4", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-25T09:12:45+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.2", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-09-10T18:51:20+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:20:53+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2023-08-03T15:11:55+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.6.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:13:57+00:00" - }, - { - "name": "influxdb/influxdb-php", - "version": "1.15.2", - "source": { - "type": "git", - "url": "https://github.com/influxdata/influxdb-php.git", - "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", - "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^6.0|^7.0", - "php": "^5.5 || ^7.0 || ^8.0" - }, - "require-dev": { - "dms/phpunit-arraysubset-asserts": "^0.2.1", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-curl": "Curl extension, needed for Curl driver", - "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." - }, - "type": "library", - "autoload": { - "psr-4": { - "InfluxDB\\": "src/InfluxDB" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Hoogendijk", - "email": "stephen@tca0.nl" - }, - { - "name": "Daniel Martinez", - "email": "danimartcas@hotmail.com" - }, - { - "name": "Gianluca Arbezzano", - "email": "gianarb92@gmail.com" - } - ], - "description": "InfluxDB client library for PHP", - "keywords": [ - "client", - "influxdata", - "influxdb", - "influxdb class", - "influxdb client", - "influxdb library", - "time series" - ], - "support": { - "issues": "https://github.com/influxdata/influxdb-php/issues", - "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" - }, - "abandoned": true, - "time": "2020-12-26T17:45:17+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^0.12.66", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" - }, - "time": "2021-10-08T21:21:46+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "league/csv", - "version": "9.7.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/csv.git", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "ext-curl": "*", - "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^2.16", - "phpstan/phpstan": "^0.12.0", - "phpstan/phpstan-phpunit": "^0.12.0", - "phpstan/phpstan-strict-rules": "^0.12.0", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", - "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "League\\Csv\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://github.com/nyamsprod/", - "role": "Developer" - } - ], - "description": "CSV data manipulation made easy in PHP", - "homepage": "http://csv.thephpleague.com", - "keywords": [ - "convert", - "csv", - "export", - "filter", - "import", - "read", - "transform", - "write" - ], - "support": { - "docs": "https://csv.thephpleague.com", - "issues": "https://github.com/thephpleague/csv/issues", - "rss": "https://github.com/thephpleague/csv/releases.atom", - "source": "https://github.com/thephpleague/csv" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2021-04-17T16:32:08+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.1.6", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "5cbea85106e561c7138d03603eb6e05128480409" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", - "reference": "5cbea85106e561c7138d03603eb6e05128480409", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2023-10-02T10:01:54+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.11.0", - "jean85/pretty-package-versions": "^1.2 || ^2.0.1", - "php": "^7.1 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "squizlabs/php_codesniffer": "^3.6", - "symfony/phpunit-bridge": "^5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" - }, - "time": "2021-10-20T22:22:37+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.8.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", - "doctrine/annotations": "^1.2.6 || ^1.13.3", - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.1", - "yoast/phpunit-polyfills": "^1.0.4" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "ext-openssl": "Needed for secure SMTP sending and DKIM signing", - "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2023-03-06T14:43:22+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" - }, - "time": "2023-04-10T20:10:41+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-23T14:45:45+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.31.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.31.1" - }, - "time": "2023-08-29T11:07:46+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.33.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.33.1" - }, - "time": "2023-08-29T11:07:40+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.43.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.3.*" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.4.*", - "pcov/clobber": "^2.0", - "phpstan/phpstan": "1.10.*", - "phpunit/phpunit": "^9.4", - "rregeer/phpunit-coverage-check": "^0.3.1", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistence using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.43.5" - }, - "time": "2023-10-06T06:49:47+00:00" - }, - { - "name": "utopia-php/domains", - "version": "0.3.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess Cope", - "email": "wess@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.3.2" - }, - "time": "2023-07-19T16:39:24+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.31.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.31.0" - }, - "time": "2023-08-30T16:10:04+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" - }, - "time": "2023-09-14T20:48:42+00:00" - }, - { - "name": "utopia-php/migration", - "version": "0.3.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/migration.git", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "shasum": "" - }, - "require": { - "appwrite/appwrite": "10.0.*", - "php": "8.*", - "utopia-php/cli": "0.*" - }, - "require-dev": { - "laravel/pint": "1.*", - "phpunit/phpunit": "9.*", - "vlucas/phpdotenv": "5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Migration\\": "src/Migration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to migrate resources between services.", - "keywords": [ - "framework", - "migration", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.3.5" - }, - "time": "2023-09-25T16:51:47+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.10.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.3.1" - }, - "time": "2023-09-01T17:25:28+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "dev-integrate-workers", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "e657da8874cbf0a5e2837357be3a4d37486d37ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/e657da8874cbf0a5e2837357be3a4d37486d37ea", - "reference": "e657da8874cbf0a5e2837357be3a4d37486d37ea", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "framework", - "php", - "platform", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/integrate-workers" - }, - "time": "2023-10-16T13:08:24+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "dev-feat-get-worker-start", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/5f81095e0ec66b2902037181ba3a20f6f9541af7", - "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" - }, - "time": "2023-10-04T14:49:13+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.17.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-xz": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.17.0" - }, - "time": "2023-08-21T11:28:36+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.7.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.7.1" - }, - "time": "2023-08-30T09:14:37+00:00" - }, - { - "name": "utopia-php/vcs", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/vcs.git", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "shasum": "" - }, - "require": { - "adhocore/jwt": "^1.1", - "php": ">=8.0", - "utopia-php/cache": "^0.8.0", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\VCS\\": "src/VCS", - "Utopia\\Detector\\": "src/Detector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", - "keywords": [ - "framework", - "php", - "utopia", - "vcs" - ], - "support": { - "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.5.0" - }, - "time": "2023-09-13T19:05:52+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.10", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-07-05T14:23:37+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.35.2", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" - }, - "time": "2023-09-14T14:59:50+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" - }, - "time": "2023-09-27T20:04:15+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.71", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2023-04-25T20:33:03+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" - }, - "time": "2023-08-12T11:01:26+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.24.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bcad8d995980440892759db0c32acae7c8e79442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", - "reference": "bcad8d995980440892759db0c32acae7c8e79442", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" - }, - "time": "2023-09-26T12:28:12+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.29", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-19T04:57:46+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/5.0.2" - }, - "time": "2023-03-20T06:05:55+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-28T09:04:16+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.7.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-08-28T11:09:02+00:00" - } - ], - "aliases": [ - { - "package": "utopia-php/platform", - "version": "dev-integrate-workers", - "alias": "0.3.3", - "alias_normalized": "0.3.3.0" - }, - { - "package": "utopia-php/queue", - "version": "dev-feat-get-worker-start", - "alias": "0.5.3", - "alias_normalized": "0.5.3.0" - } - ], - "minimum-stability": "stable", - "stability-flags": { - "utopia-php/platform": 20, - "utopia-php/queue": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.2.0" -} From 6d98b38e238948ed8c014497a379f30c7ce7c753 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 07:29:37 +0300 Subject: [PATCH 106/144] addressing some comments --- composer.lock | 6023 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6023 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..7ccc6df4df --- /dev/null +++ b/composer.lock @@ -0,0 +1,6023 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e34e098996ca4769e2a956b423522e91", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/appwrite", + "version": "10.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-for-php.git", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\": "src/Appwrite" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", + "support": { + "email": "team@appwrite.io", + "issues": "https://github.com/appwrite/sdk-for-php/issues", + "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", + "url": "https://appwrite.io/support" + }, + "time": "2023-09-07T23:28:31+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "f3897169f5c1f365312238a516ae9465f804634f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", + "reference": "f3897169f5c1f365312238a516ae9465f804634f", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" + }, + "time": "2023-02-24T09:50:42+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.13.1", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.7.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2023-10-16T15:39:53+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1.4", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-25T09:12:45+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:20:53+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-08-03T15:11:55+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:13:57+00:00" + }, + { + "name": "influxdb/influxdb-php", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/influxdata/influxdb-php.git", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": "^5.5 || ^7.0 || ^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "Curl extension, needed for Curl driver", + "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB\\": "src/InfluxDB" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Hoogendijk", + "email": "stephen@tca0.nl" + }, + { + "name": "Daniel Martinez", + "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" + } + ], + "description": "InfluxDB client library for PHP", + "keywords": [ + "client", + "influxdata", + "influxdb", + "influxdb class", + "influxdb client", + "influxdb library", + "time series" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-php/issues", + "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" + }, + "abandoned": true, + "time": "2020-12-26T17:45:17+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "league/csv", + "version": "9.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.0", + "phpstan/phpstan-phpunit": "^0.12.0", + "phpstan/phpstan-strict-rules": "^0.12.0", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2021-04-17T16:32:08+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.1.6", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "5cbea85106e561c7138d03603eb6e05128480409" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", + "reference": "5cbea85106e561c7138d03603eb6e05128480409", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2023-10-02T10:01:54+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.11.0", + "jean85/pretty-package-versions": "^1.2 || ^2.0.1", + "php": "^7.1 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "squizlabs/php_codesniffer": "^3.6", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + }, + "time": "2021-10-20T22:22:37+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2023-03-06T14:43:22+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.31.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.31.1" + }, + "time": "2023-08-29T11:07:46+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.33.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.33.1" + }, + "time": "2023-08-29T11:07:40+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.43.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.3.*" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.4.*", + "pcov/clobber": "^2.0", + "phpstan/phpstan": "1.10.*", + "phpunit/phpunit": "^9.4", + "rregeer/phpunit-coverage-check": "^0.3.1", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistence using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.43.5" + }, + "time": "2023-10-06T06:49:47+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/0.3.2" + }, + "time": "2023-07-19T16:39:24+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.31.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.31.0" + }, + "time": "2023-08-30T16:10:04+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + }, + "time": "2023-09-14T20:48:42+00:00" + }, + { + "name": "utopia-php/migration", + "version": "0.3.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/migration.git", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "shasum": "" + }, + "require": { + "appwrite/appwrite": "10.0.*", + "php": "8.*", + "utopia-php/cli": "0.*" + }, + "require-dev": { + "laravel/pint": "1.*", + "phpunit/phpunit": "9.*", + "vlucas/phpdotenv": "5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Migration\\": "src/Migration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to migrate resources between services.", + "keywords": [ + "framework", + "migration", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/0.3.5" + }, + "time": "2023-09-25T16:51:47+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.10.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.3.1" + }, + "time": "2023-09-01T17:25:28+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/229a7b1fa1f39afd1532f7a515326a6afc222a26", + "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "framework", + "php", + "platform", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/0.5.0" + }, + "time": "2023-10-16T20:28:49+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "0.5.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/0.5.3" + }, + "time": "2023-05-24T19:06:04+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.17.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-xz": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.17.0" + }, + "time": "2023-08-21T11:28:36+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.7.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.7.1" + }, + "time": "2023-08-30T09:14:37+00:00" + }, + { + "name": "utopia-php/vcs", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/vcs.git", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "shasum": "" + }, + "require": { + "adhocore/jwt": "^1.1", + "php": ">=8.0", + "utopia-php/cache": "^0.8.0", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\VCS\\": "src/VCS", + "Utopia\\Detector\\": "src/Detector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", + "keywords": [ + "framework", + "php", + "utopia", + "vcs" + ], + "support": { + "issues": "https://github.com/utopia-php/vcs/issues", + "source": "https://github.com/utopia-php/vcs/tree/0.5.0" + }, + "time": "2023-09-13T19:05:52+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.10", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-07-05T14:23:37+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.35.2", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" + }, + "time": "2023-09-14T14:59:50+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.71", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2023-04-25T20:33:03+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "bcad8d995980440892759db0c32acae7c8e79442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + }, + "time": "2023-09-26T12:28:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-19T04:57:46+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/5.0.2" + }, + "time": "2023-03-20T06:05:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-08-28T11:09:02+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From 09f829e4829f52b071bebe43f6df4fa6ad888c0b Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 11:00:14 +0300 Subject: [PATCH 107/144] updating gitmodules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 8ec131892e..e85f63dc9a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.1 + branch = 3.2.0 From 20468782e5866dd4060e45a798452f270b1824f9 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 08:35:22 +0000 Subject: [PATCH 108/144] chore: revert console --- app/console | 1 + 1 file changed, 1 insertion(+) create mode 160000 app/console diff --git a/app/console b/app/console new file mode 160000 index 0000000000..c57a776730 --- /dev/null +++ b/app/console @@ -0,0 +1 @@ +Subproject commit c57a776730fff4f11b36de70d53514a7614753aa From 022952e2ee62a171b9358d0d3e88228b4b84f7fd Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 08:36:41 +0000 Subject: [PATCH 109/144] chore: revert console --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index e85f63dc9a..8ec131892e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.0 + branch = 3.2.1 diff --git a/app/console b/app/console index c57a776730..2f47e4e77b 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit c57a776730fff4f11b36de70d53514a7614753aa +Subproject commit 2f47e4e77b3c832679c5a83a604dd7b8e8fc1903 From 3d5930c10449f28b81fcbf5a00dc3e7eebfefb85 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 16:55:08 +0300 Subject: [PATCH 110/144] some fixes --- app/views/install/compose.phtml | 2 -- app/worker.php | 11 +++++++++-- docker-compose.yml | 4 ++-- tests/resources/functions/php-event/index.php | 8 ++++++++ 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 tests/resources/functions/php-event/index.php diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 11fb5dae61..8928107029 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -345,8 +345,6 @@ services: - _APP_DB_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG - - _APP_WORKERS_NUM=1 - - _APP_QUEUE_NAME=database_db_main appwrite-worker-builds: image: /: diff --git a/app/worker.php b/app/worker.php index a991ed6f5f..4d11aba1bd 100644 --- a/app/worker.php +++ b/app/worker.php @@ -204,16 +204,23 @@ try { * - _APP_WORKER_PER_CORE The number of worker processes per core (ignored if _APP_WORKERS_NUM is set) * - _APP_QUEUE_NAME The name of the queue to read for database events */ + if ($workerName === 'databases') { + $queueName = App::getEnv('_APP_QUEUE_NAME', 'database_db_main'); + } else { + $queueName = App::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)); + } + $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => App::getEnv('_APP_WORKERS_NUM', swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6))), + 'workersNum' => App::getEnv('_APP_WORKERS_NUM', 1), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, - 'queueName' => App::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)) + 'queueName' => $queueName ]); } catch (\Exception $e) { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } + $worker = $platform->getWorker(); $worker diff --git a/docker-compose.yml b/docker-compose.yml index 15d56fc184..b5dd37aa96 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -379,8 +379,8 @@ services: - _APP_DB_PASS - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG - - _APP_WORKERS_NUM=1 - - _APP_QUEUE_NAME=database_db_main + - _APP_WORKERS_NUM + - _APP_QUEUE_NAME appwrite-worker-builds: entrypoint: worker-builds diff --git a/tests/resources/functions/php-event/index.php b/tests/resources/functions/php-event/index.php new file mode 100644 index 0000000000..550fd57729 --- /dev/null +++ b/tests/resources/functions/php-event/index.php @@ -0,0 +1,8 @@ +log($context->req->body['$id']); + $context->log($context->req->body['name']); + + return $context->res->empty(); +}; From b42a9e94e2cd242762e5720d9df43937beb19b53 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 18:12:24 +0300 Subject: [PATCH 111/144] some fixes --- composer.json | 2 +- composer.lock | 6023 -------------------------------------------- docker-compose.yml | 4 +- 3 files changed, 3 insertions(+), 6026 deletions(-) delete mode 100644 composer.lock diff --git a/composer.json b/composer.json index f7340a69ba..bd5b143f16 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "utopia-php/image": "0.5.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", - "utopia-php/messaging": "0.2.*", + "utopia-php/messaging": "0.1.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.5.*", diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 7ccc6df4df..0000000000 --- a/composer.lock +++ /dev/null @@ -1,6023 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "e34e098996ca4769e2a956b423522e91", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/appwrite", - "version": "10.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\": "src/Appwrite" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", - "support": { - "email": "team@appwrite.io", - "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", - "url": "https://appwrite.io/support" - }, - "time": "2023-09-07T23:28:31+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "f3897169f5c1f365312238a516ae9465f804634f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", - "reference": "f3897169f5c1f365312238a516ae9465f804634f", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" - }, - "time": "2023-02-24T09:50:42+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.13.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.7.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2023-10-16T15:39:53+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1.4", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-25T09:12:45+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.2", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-09-10T18:51:20+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:20:53+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2023-08-03T15:11:55+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.6.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:13:57+00:00" - }, - { - "name": "influxdb/influxdb-php", - "version": "1.15.2", - "source": { - "type": "git", - "url": "https://github.com/influxdata/influxdb-php.git", - "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", - "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^6.0|^7.0", - "php": "^5.5 || ^7.0 || ^8.0" - }, - "require-dev": { - "dms/phpunit-arraysubset-asserts": "^0.2.1", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-curl": "Curl extension, needed for Curl driver", - "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." - }, - "type": "library", - "autoload": { - "psr-4": { - "InfluxDB\\": "src/InfluxDB" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Hoogendijk", - "email": "stephen@tca0.nl" - }, - { - "name": "Daniel Martinez", - "email": "danimartcas@hotmail.com" - }, - { - "name": "Gianluca Arbezzano", - "email": "gianarb92@gmail.com" - } - ], - "description": "InfluxDB client library for PHP", - "keywords": [ - "client", - "influxdata", - "influxdb", - "influxdb class", - "influxdb client", - "influxdb library", - "time series" - ], - "support": { - "issues": "https://github.com/influxdata/influxdb-php/issues", - "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" - }, - "abandoned": true, - "time": "2020-12-26T17:45:17+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^0.12.66", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" - }, - "time": "2021-10-08T21:21:46+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "league/csv", - "version": "9.7.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/csv.git", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "ext-curl": "*", - "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^2.16", - "phpstan/phpstan": "^0.12.0", - "phpstan/phpstan-phpunit": "^0.12.0", - "phpstan/phpstan-strict-rules": "^0.12.0", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", - "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "League\\Csv\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://github.com/nyamsprod/", - "role": "Developer" - } - ], - "description": "CSV data manipulation made easy in PHP", - "homepage": "http://csv.thephpleague.com", - "keywords": [ - "convert", - "csv", - "export", - "filter", - "import", - "read", - "transform", - "write" - ], - "support": { - "docs": "https://csv.thephpleague.com", - "issues": "https://github.com/thephpleague/csv/issues", - "rss": "https://github.com/thephpleague/csv/releases.atom", - "source": "https://github.com/thephpleague/csv" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2021-04-17T16:32:08+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.1.6", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "5cbea85106e561c7138d03603eb6e05128480409" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", - "reference": "5cbea85106e561c7138d03603eb6e05128480409", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2023-10-02T10:01:54+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.11.0", - "jean85/pretty-package-versions": "^1.2 || ^2.0.1", - "php": "^7.1 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "squizlabs/php_codesniffer": "^3.6", - "symfony/phpunit-bridge": "^5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" - }, - "time": "2021-10-20T22:22:37+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.8.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", - "doctrine/annotations": "^1.2.6 || ^1.13.3", - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.1", - "yoast/phpunit-polyfills": "^1.0.4" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "ext-openssl": "Needed for secure SMTP sending and DKIM signing", - "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2023-03-06T14:43:22+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" - }, - "time": "2023-04-10T20:10:41+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-23T14:45:45+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.31.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.31.1" - }, - "time": "2023-08-29T11:07:46+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.33.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.33.1" - }, - "time": "2023-08-29T11:07:40+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.43.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.3.*" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.4.*", - "pcov/clobber": "^2.0", - "phpstan/phpstan": "1.10.*", - "phpunit/phpunit": "^9.4", - "rregeer/phpunit-coverage-check": "^0.3.1", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistence using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.43.5" - }, - "time": "2023-10-06T06:49:47+00:00" - }, - { - "name": "utopia-php/domains", - "version": "0.3.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess Cope", - "email": "wess@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.3.2" - }, - "time": "2023-07-19T16:39:24+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.31.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.31.0" - }, - "time": "2023-08-30T16:10:04+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" - }, - "time": "2023-09-14T20:48:42+00:00" - }, - { - "name": "utopia-php/migration", - "version": "0.3.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/migration.git", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "shasum": "" - }, - "require": { - "appwrite/appwrite": "10.0.*", - "php": "8.*", - "utopia-php/cli": "0.*" - }, - "require-dev": { - "laravel/pint": "1.*", - "phpunit/phpunit": "9.*", - "vlucas/phpdotenv": "5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Migration\\": "src/Migration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to migrate resources between services.", - "keywords": [ - "framework", - "migration", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.3.5" - }, - "time": "2023-09-25T16:51:47+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.10.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.3.1" - }, - "time": "2023-09-01T17:25:28+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/229a7b1fa1f39afd1532f7a515326a6afc222a26", - "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "framework", - "php", - "platform", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.5.0" - }, - "time": "2023-10-16T20:28:49+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "0.5.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.3" - }, - "time": "2023-05-24T19:06:04+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.17.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-xz": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.17.0" - }, - "time": "2023-08-21T11:28:36+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.7.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.7.1" - }, - "time": "2023-08-30T09:14:37+00:00" - }, - { - "name": "utopia-php/vcs", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/vcs.git", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "shasum": "" - }, - "require": { - "adhocore/jwt": "^1.1", - "php": ">=8.0", - "utopia-php/cache": "^0.8.0", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\VCS\\": "src/VCS", - "Utopia\\Detector\\": "src/Detector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", - "keywords": [ - "framework", - "php", - "utopia", - "vcs" - ], - "support": { - "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.5.0" - }, - "time": "2023-09-13T19:05:52+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.10", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-07-05T14:23:37+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.35.2", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" - }, - "time": "2023-09-14T14:59:50+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" - }, - "time": "2023-09-27T20:04:15+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.71", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2023-04-25T20:33:03+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" - }, - "time": "2023-08-12T11:01:26+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.24.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bcad8d995980440892759db0c32acae7c8e79442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", - "reference": "bcad8d995980440892759db0c32acae7c8e79442", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" - }, - "time": "2023-09-26T12:28:12+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.29", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-19T04:57:46+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/5.0.2" - }, - "time": "2023-03-20T06:05:55+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-28T09:04:16+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.7.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-08-28T11:09:02+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.2.0" -} diff --git a/docker-compose.yml b/docker-compose.yml index b5dd37aa96..4e34b1bad9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,7 +83,7 @@ services: - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src - - ./dev:/usr/local/dev + - ./dev:/usr/src/code/dev depends_on: - mariadb - redis @@ -360,7 +360,7 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./src:/usr/src/code/srcrm + - ./src:/usr/src/code/src depends_on: - redis - mariadb From 59052b4fd171e43c7a07e1943f7e12861aa588ea Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 18:12:43 +0300 Subject: [PATCH 112/144] some fixes --- composer.lock | 6023 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6023 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..60a8bf6393 --- /dev/null +++ b/composer.lock @@ -0,0 +1,6023 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5060c891e467051fedd25f3853cd37b0", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/appwrite", + "version": "10.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-for-php.git", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\": "src/Appwrite" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", + "support": { + "email": "team@appwrite.io", + "issues": "https://github.com/appwrite/sdk-for-php/issues", + "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", + "url": "https://appwrite.io/support" + }, + "time": "2023-09-07T23:28:31+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "f3897169f5c1f365312238a516ae9465f804634f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", + "reference": "f3897169f5c1f365312238a516ae9465f804634f", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" + }, + "time": "2023-02-24T09:50:42+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.13.1", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.7.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2023-10-16T15:39:53+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1.4", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-25T09:12:45+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:20:53+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-08-03T15:11:55+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:13:57+00:00" + }, + { + "name": "influxdb/influxdb-php", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/influxdata/influxdb-php.git", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": "^5.5 || ^7.0 || ^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "Curl extension, needed for Curl driver", + "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB\\": "src/InfluxDB" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Hoogendijk", + "email": "stephen@tca0.nl" + }, + { + "name": "Daniel Martinez", + "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" + } + ], + "description": "InfluxDB client library for PHP", + "keywords": [ + "client", + "influxdata", + "influxdb", + "influxdb class", + "influxdb client", + "influxdb library", + "time series" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-php/issues", + "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" + }, + "abandoned": true, + "time": "2020-12-26T17:45:17+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "league/csv", + "version": "9.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.0", + "phpstan/phpstan-phpunit": "^0.12.0", + "phpstan/phpstan-strict-rules": "^0.12.0", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2021-04-17T16:32:08+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.1.6", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "5cbea85106e561c7138d03603eb6e05128480409" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", + "reference": "5cbea85106e561c7138d03603eb6e05128480409", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2023-10-02T10:01:54+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.11.0", + "jean85/pretty-package-versions": "^1.2 || ^2.0.1", + "php": "^7.1 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "squizlabs/php_codesniffer": "^3.6", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + }, + "time": "2021-10-20T22:22:37+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2023-03-06T14:43:22+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.31.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.31.1" + }, + "time": "2023-08-29T11:07:46+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.33.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.33.1" + }, + "time": "2023-08-29T11:07:40+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.43.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.3.*" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.4.*", + "pcov/clobber": "^2.0", + "phpstan/phpstan": "1.10.*", + "phpunit/phpunit": "^9.4", + "rregeer/phpunit-coverage-check": "^0.3.1", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistence using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.43.5" + }, + "time": "2023-10-06T06:49:47+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/0.3.2" + }, + "time": "2023-07-19T16:39:24+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.31.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.31.0" + }, + "time": "2023-08-30T16:10:04+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + }, + "time": "2023-02-07T05:42:46+00:00" + }, + { + "name": "utopia-php/migration", + "version": "0.3.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/migration.git", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "shasum": "" + }, + "require": { + "appwrite/appwrite": "10.0.*", + "php": "8.*", + "utopia-php/cli": "0.*" + }, + "require-dev": { + "laravel/pint": "1.*", + "phpunit/phpunit": "9.*", + "vlucas/phpdotenv": "5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Migration\\": "src/Migration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to migrate resources between services.", + "keywords": [ + "framework", + "migration", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/0.3.5" + }, + "time": "2023-09-25T16:51:47+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.10.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.3.1" + }, + "time": "2023-09-01T17:25:28+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/229a7b1fa1f39afd1532f7a515326a6afc222a26", + "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "framework", + "php", + "platform", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/0.5.0" + }, + "time": "2023-10-16T20:28:49+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "0.5.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/0.5.3" + }, + "time": "2023-05-24T19:06:04+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.17.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-xz": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.17.0" + }, + "time": "2023-08-21T11:28:36+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.7.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.7.1" + }, + "time": "2023-08-30T09:14:37+00:00" + }, + { + "name": "utopia-php/vcs", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/vcs.git", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "shasum": "" + }, + "require": { + "adhocore/jwt": "^1.1", + "php": ">=8.0", + "utopia-php/cache": "^0.8.0", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\VCS\\": "src/VCS", + "Utopia\\Detector\\": "src/Detector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", + "keywords": [ + "framework", + "php", + "utopia", + "vcs" + ], + "support": { + "issues": "https://github.com/utopia-php/vcs/issues", + "source": "https://github.com/utopia-php/vcs/tree/0.5.0" + }, + "time": "2023-09-13T19:05:52+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.10", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-07-05T14:23:37+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.35.2", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" + }, + "time": "2023-09-14T14:59:50+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.71", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2023-04-25T20:33:03+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "bcad8d995980440892759db0c32acae7c8e79442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + }, + "time": "2023-09-26T12:28:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-19T04:57:46+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/5.0.2" + }, + "time": "2023-03-20T06:05:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-08-28T11:09:02+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From 8b6d050b0dd2deaa54a524bb198e54dc284dc56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 17 Oct 2023 18:23:10 +0200 Subject: [PATCH 113/144] Fix failing event tests --- src/Appwrite/Platform/Workers/Functions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index d7607d626b..6b7b5efe03 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -125,7 +125,8 @@ class Functions extends Action path: '/', method: 'POST', headers: [ - 'user-agent' => 'Appwrite/' . APP_VERSION_STABLE + 'user-agent' => 'Appwrite/' . APP_VERSION_STABLE, + 'content-type' => 'application/json' ], data: null, user: $user, From 1036cb406df6e3f39a20063921780c6597c58ab0 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 21:32:38 +0300 Subject: [PATCH 114/144] some fixes --- app/init.php | 2 - src/Appwrite/Platform/Tasks/CalcTierStats.php | 3 +- src/Appwrite/Platform/Tasks/Schedule.php | 4 - .../Platform/Workers/Certificates.php | 1 + src/Appwrite/Platform/Workers/Deletes.php | 177 +++++++++++------- .../Databases/DatabasesConsoleClientTest.php | 7 +- 6 files changed, 113 insertions(+), 81 deletions(-) diff --git a/app/init.php b/app/init.php index f977394ba4..bc1e95e077 100644 --- a/app/init.php +++ b/app/init.php @@ -18,7 +18,6 @@ ini_set('display_startup_errors', 1); ini_set('default_socket_timeout', -1); error_reporting(E_ALL); -use Appwrite\Event\Usage; use Appwrite\Event\Migration; use Appwrite\Extend\Exception; use Appwrite\Auth\Auth; @@ -70,7 +69,6 @@ use Utopia\Pools\Group; use Utopia\Pools\Pool; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; -use Appwrite\Auth\OAuth2\Github; use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Func; diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php index b614f4c8be..2a2bc20af9 100644 --- a/src/Appwrite/Platform/Tasks/CalcTierStats.php +++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php @@ -200,9 +200,8 @@ class CalcTierStats extends Action try { /** Get Domains */ - $stats['Domains'] = $dbForConsole->count('rules', [ + $stats['Domains'] = $dbForConsole->count('domains', [ Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(APP_LIMIT_COUNT) ]); } catch (\Throwable) { $stats['Domains'] = 0; diff --git a/src/Appwrite/Platform/Tasks/Schedule.php b/src/Appwrite/Platform/Tasks/Schedule.php index 57c9d22c4f..a136ee62b1 100644 --- a/src/Appwrite/Platform/Tasks/Schedule.php +++ b/src/Appwrite/Platform/Tasks/Schedule.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Tasks; -use Appwrite\Extend\Exception; use Cron\CronExpression; use Swoole\Timer; use Utopia\App; @@ -27,9 +26,6 @@ class Schedule extends Action return 'schedule'; } - /** - * @throws Exception - */ public function __construct() { $this diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 3ecc845a96..8fb5094a8f 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -213,6 +213,7 @@ class Certificates extends Action $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); } else { + $certificate->removeAttribute('$internalId'); $certificate = $dbForConsole->createDocument('certificates', $certificate); } diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 896ad40bf9..7e43bed6bd 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -22,6 +22,7 @@ use Utopia\Database\Exception\Structure; use Utopia\Database\Query; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Storage\Device; class Deletes extends Action { @@ -100,7 +101,7 @@ class Deletes extends Action $this->deleteInstallation($dbForConsole, $getProjectDB, $document, $project); break; case DELETE_TYPE_RULES: - $this->deleteRule($dbForConsole, $document, $project); + $this->deleteRule($dbForConsole, $document); break; default: if (\str_starts_with($document->getCollection(), 'database_')) { @@ -130,7 +131,7 @@ class Deletes extends Action break; case DELETE_TYPE_REALTIME: - $this->deleteRealtimeUsage($dbForConsole, $getProjectDB, $datetime); + $this->deleteRealtimeUsage($dbForConsole, $datetime); break; case DELETE_TYPE_SESSIONS: @@ -162,7 +163,7 @@ class Deletes extends Action * @throws Authorization * @throws Throwable */ - protected function deleteSchedules(Database $dbForConsole, callable $getProjectDB, string $datetime): void + private function deleteSchedules(Database $dbForConsole, callable $getProjectDB, string $datetime): void { $this->listByGroup( 'schedules', @@ -198,7 +199,7 @@ class Deletes extends Action * @return void * @throws Authorization */ - protected function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void + private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -234,7 +235,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteCacheByDate(Document $project, callable $getProjectDB, string $datetime): void + private function deleteCacheByDate(Document $project, callable $getProjectDB, string $datetime): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -270,7 +271,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteDatabase(callable $getProjectDB, Document $document, Document $project): void + private function deleteDatabase(callable $getProjectDB, Document $document, Document $project): void { $databaseId = $document->getId(); $dbForProject = $getProjectDB($project); @@ -290,7 +291,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteCollection(callable $getProjectDB, Document $document, Document $project): void + private function deleteCollection(callable $getProjectDB, Document $document, Document $project): void { $collectionId = $document->getId(); $collectionInternalId = $document->getInternalId(); @@ -336,7 +337,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteUsageStats(Database $dbForConsole, callable $getProjectDB, string $hourlyUsageRetentionDatetime): void + private function deleteUsageStats(Database $dbForConsole, callable $getProjectDB, string $hourlyUsageRetentionDatetime): void { $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $hourlyUsageRetentionDatetime) { $dbForProject = $getProjectDB($project); @@ -355,7 +356,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void + private function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void { $dbForProject = $getProjectDB($project); $teamInternalId = $document->getInternalId(); @@ -384,7 +385,7 @@ class Deletes extends Action * @throws Restricted * @throws Structure */ - protected function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void + private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void { $projects = $dbForConsole->find('projects', [ @@ -409,7 +410,7 @@ class Deletes extends Action * @throws Authorization * @throws \Utopia\Database\Exception */ - protected function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void + private function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void { $projectId = $document->getId(); $projectInternalId = $document->getInternalId(); @@ -434,10 +435,12 @@ class Deletes extends Action Query::equal('projectInternalId', [$projectInternalId]) ], $dbForConsole); - // Delete Domains - $this->deleteByGroup('domains', [ + // Delete project and function rules + $this->deleteByGroup('rules', [ Query::equal('projectInternalId', [$projectInternalId]) - ], $dbForConsole); + ], $dbForConsole, function (Document $document) use ($dbForConsole) { + $this->deleteRule($dbForConsole, $document); + }); // Delete Keys $this->deleteByGroup('keys', [ @@ -476,7 +479,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteUser(callable $getProjectDB, Document $document, Document $project): void + private function deleteUser(callable $getProjectDB, Document $document, Document $project): void { $userId = $document->getId(); $userInternalId = $document->getInternalId(); @@ -525,7 +528,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteExecutionLogs(database $dbForConsole, callable $getProjectDB, string $datetime): void + private function deleteExecutionLogs(database $dbForConsole, callable $getProjectDB, string $datetime): void { $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($getProjectDB, $datetime) { $dbForProject = $getProjectDB($project); @@ -542,7 +545,7 @@ class Deletes extends Action * @return void * @throws Exception|Throwable */ - protected function deleteExpiredSessions(Database $dbForConsole, callable $getProjectDB): void + private function deleteExpiredSessions(Database $dbForConsole, callable $getProjectDB): void { $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($dbForConsole, $getProjectDB) { @@ -560,20 +563,16 @@ class Deletes extends Action /** * @param Database $dbForConsole - * @param callable $getProjectDB * @param string $datetime * @return void * @throws Exception */ - protected function deleteRealtimeUsage(Database $dbForConsole, callable $getProjectDB, string $datetime): void + private function deleteRealtimeUsage(Database $dbForConsole, string $datetime): void { - $this->deleteForProjectIds($dbForConsole, function (Document $project) use ($datetime, $getProjectDB) { - $dbForProject = $getProjectDB($project); - // Delete Dead Realtime Logs - $this->deleteByGroup('realtime', [ - Query::lessThan('timestamp', $datetime) - ], $dbForProject); - }); + // Delete Dead Realtime Logs + $this->deleteByGroup('realtime', [ + Query::lessThan('timestamp', $datetime) + ], $dbForConsole); } /** @@ -583,7 +582,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteAbuseLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void + private function deleteAbuseLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void { if (empty($datetime)) { throw new Exception('Failed to delete audit logs. No datetime provided'); @@ -608,7 +607,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteAuditLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void + private function deleteAuditLogs(Database $dbForConsole, callable $getProjectDB, string $datetime): void { if (empty($datetime)) { throw new Exception('Failed to delete audit logs. No datetime provided'); @@ -632,7 +631,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteAuditLogsByResource(callable $getProjectDB, string $resource, Document $project): void + private function deleteAuditLogsByResource(callable $getProjectDB, string $resource, Document $project): void { $dbForProject = $getProjectDB($project); @@ -650,7 +649,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteFunction(Database $dbForConsole, callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + private function deleteFunction(Database $dbForConsole, callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -666,7 +665,7 @@ class Deletes extends Action Query::equal('resourceInternalId', [$functionInternalId]), Query::equal('projectInternalId', [$project->getInternalId()]) ], $dbForConsole, function (Document $document) use ($project, $dbForConsole) { - $this->deleteRule($dbForConsole, $document, $project); + $this->deleteRule($dbForConsole, $document); }); /** @@ -688,14 +687,10 @@ class Deletes extends Action Query::equal('resourceInternalId', [$functionInternalId]) ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentInternalIds) { $deploymentInternalIds[] = $document->getInternalId(); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } + $this->deleteDeploymentFiles($storageFunctions, $document); }); - /** + /** * Delete builds */ Console::info("Deleting builds for function " . $functionId); @@ -704,11 +699,7 @@ class Deletes extends Action $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) ], $dbForProject, function (Document $document) use ($storageBuilds) { - if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); - } + $this->deleteBuildFiles($storageBuilds, $document); }); } @@ -727,6 +718,68 @@ class Deletes extends Action $this->deleteRuntimes($getProjectDB, $document, $project); } + /** + * @param Device $device + * @param Document $deployment + * @return void + */ + private function deleteDeploymentFiles(Device $device, Document $deployment): void + { + $deploymentId = $deployment->getId(); + $deploymentPath = $deployment->getAttribute('path', ''); + + if (empty($deploymentPath)) { + Console::info("No deployment files for deployment " . $deploymentId); + return; + } + + Console::info("Deleting deployment files for deployment " . $deploymentId); + + try { + if ($device->delete($deploymentPath, true)) { + Console::success('Deleted deployment files: ' . $deploymentPath); + } else { + Console::error('Failed to delete deployment files: ' . $deploymentPath); + } + } catch (\Throwable $th) { + Console::error('Failed to delete deployment files: ' . $deploymentPath); + Console::error('[Error] Type: ' . get_class($th)); + Console::error('[Error] Message: ' . $th->getMessage()); + Console::error('[Error] File: ' . $th->getFile()); + Console::error('[Error] Line: ' . $th->getLine()); + } + } + + /** + * @param Device $device + * @param Document $build + * @return void + */ + private function deleteBuildFiles(Device $device, Document $build): void + { + $buildId = $build->getId(); + $buildPath = $build->getAttribute('path', ''); + + if (empty($buildPath)) { + Console::info("No build files for build " . $buildId); + return; + } + + try { + if ($device->delete($buildPath, true)) { + Console::success('Deleted build files: ' . $buildPath); + } else { + Console::error('Failed to delete build files: ' . $buildPath); + } + } catch (\Throwable $th) { + Console::error('Failed to delete deployment files: ' . $buildPath); + Console::error('[Error] Type: ' . get_class($th)); + Console::error('[Error] Message: ' . $th->getMessage()); + Console::error('[Error] File: ' . $th->getFile()); + Console::error('[Error] Line: ' . $th->getLine()); + } + } + /** * @param callable $getProjectDB * @param callable $getFunctionsDevice @@ -736,7 +789,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + private function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -746,30 +799,20 @@ class Deletes extends Action /** * Delete deployment files */ - Console::info("Deleting deployment files for deployment " . $deploymentId); $storageFunctions = $getFunctionsDevice($projectId); - if ($storageFunctions->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted deployment files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete deployment files: ' . $document->getAttribute('path', '')); - } + $this->deleteDeploymentFiles($storageFunctions, $document); /** * Delete builds */ Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $getBuildsDevice($projectId); + $storageBuilds = $$getBuildsDevice($projectId); $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) ], $dbForProject, function (Document $document) use ($storageBuilds) { - if ($storageBuilds->delete($document->getAttribute('path', ''), true)) { - Console::success('Deleted build files: ' . $document->getAttribute('path', '')); - } else { - Console::error('Failed to delete build files: ' . $document->getAttribute('path', '')); - } + $this->deleteBuildFiles($storageBuilds, $document); }); - /** * Request executor to delete all deployment containers */ @@ -781,9 +824,9 @@ class Deletes extends Action * @param Document $document to be deleted * @param Database $database to delete it from * @param callable|null $callback to perform after document is deleted - * @return bool + * @return void */ - protected function deleteById(Document $document, Database $database, callable $callback = null): bool + private function deleteById(Document $document, Database $database, callable $callback = null): void { if ($database->deleteDocument($document->getCollection(), $document->getId())) { Console::success('Deleted document "' . $document->getId() . '" successfully'); @@ -791,11 +834,8 @@ class Deletes extends Action if (is_callable($callback)) { $callback($document); } - - return true; } else { Console::error('Failed to delete document: ' . $document->getId()); - return false; } } @@ -804,7 +844,7 @@ class Deletes extends Action * @param callable $callback * @throws Exception */ - protected function deleteForProjectIds(database $dbForConsole, callable $callback): void + private function deleteForProjectIds(database $dbForConsole, callable $callback): void { // TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document $count = 0; @@ -840,7 +880,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void + private function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void { $count = 0; $chunk = 0; @@ -877,7 +917,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void + private function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void { $count = 0; $chunk = 0; @@ -910,10 +950,9 @@ class Deletes extends Action /** * @param Database $dbForConsole * @param Document $document rule document - * @param Document $project project document * @return void */ - protected function deleteRule(Database $dbForConsole, Document $document, Document $project): void + private function deleteRule(Database $dbForConsole, Document $document): void { $domain = $document->getAttribute('domain'); @@ -942,7 +981,7 @@ class Deletes extends Action * @param Document $project * @return void */ - protected function deleteBucket(callable $getProjectDB, callable $getFilesDevice, Document $document, Document $project): void + private function deleteBucket(callable $getProjectDB, callable $getFilesDevice, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -962,7 +1001,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteInstallation(Database $dbForConsole, callable $getProjectDB, Document $document, Document $project): void + private function deleteInstallation(Database $dbForConsole, callable $getProjectDB, Document $document, Document $project): void { $dbForProject = $getProjectDB($project); @@ -991,7 +1030,7 @@ class Deletes extends Action * @return void * @throws Exception */ - protected function deleteRuntimes(callable $getProjectDB, ?Document $function, Document $project): void + private function deleteRuntimes(callable $getProjectDB, ?Document $function, Document $project): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index 04ac00c263..91a0d089ae 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -263,7 +263,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'limit' => 1 + 'queries' => ['limit(1)'] ]); $this->assertEquals(200, $logs['headers']['status-code']); @@ -275,7 +275,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'offset' => 1 + 'queries' => ['offset(1)'] ]); $this->assertEquals(200, $logs['headers']['status-code']); @@ -286,8 +286,7 @@ class DatabasesConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'offset' => 1, - 'limit' => 1 + 'queries' => ['offset(1)', 'limit(1)'] ]); $this->assertEquals(200, $logs['headers']['status-code']); From 593e5ab3163d588a07205a23ea2f1026cf4379bc Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 17 Oct 2023 22:08:01 +0300 Subject: [PATCH 115/144] some fixes --- app/controllers/api/account.php | 2 +- app/worker.php | 11 +++++++---- src/Appwrite/Event/Build.php | 2 -- src/Appwrite/Event/Database.php | 1 - src/Appwrite/Event/Delete.php | 2 -- src/Appwrite/Event/Event.php | 22 ++++++++++++++++++++++ src/Appwrite/Event/Func.php | 4 ++++ src/Appwrite/Event/Mail.php | 1 - src/Appwrite/Event/Migration.php | 3 --- src/Appwrite/Event/Phone.php | 2 -- 10 files changed, 34 insertions(+), 16 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 4494208078..2bc7759620 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2854,7 +2854,7 @@ App::put('/v1/account/verification') $dbForProject->deleteCachedDocument('users', $profile->getId()); $queueForEvents - ->setParam('userId', $user->getId()) + ->setParam('userId', $userId) ->setParam('tokenId', $verificationDocument->getId()) ; diff --git a/app/worker.php b/app/worker.php index 4d11aba1bd..8aa52ab931 100644 --- a/app/worker.php +++ b/app/worker.php @@ -44,7 +44,8 @@ Server::setResource('dbForConsole', function (Cache $cache, Registry $register) $database = $pools ->get('console') ->pop() - ->getResource(); + ->getResource() + ; $adapter = new Database($database, $cache); $adapter->setNamespace('_console'); @@ -64,9 +65,10 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $database = $pools ->get($project->getAttribute('database')) ->pop() - ->getResource(); + ->getResource() + ; - $adapter = new Database($database, $cache); + $adapter = new Database($database, $cache); $adapter->setNamespace('_' . $project->getInternalId()); return $adapter; }, ['cache', 'register', 'message', 'dbForConsole']); @@ -111,7 +113,8 @@ Server::setResource('cache', function (Registry $register) { $adapters[] = $pools ->get($value) ->pop() - ->getResource(); + ->getResource() + ; } return new Cache(new Sharding($adapters)); diff --git a/src/Appwrite/Event/Build.php b/src/Appwrite/Event/Build.php index 58667344ac..496db87d64 100644 --- a/src/Appwrite/Event/Build.php +++ b/src/Appwrite/Event/Build.php @@ -114,8 +114,6 @@ class Build extends Event { $client = new Client($this->queue, $this->connection); - $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; - return $client->enqueue([ 'project' => $this->project, 'resource' => $this->resource, diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index eb22f8897f..e5a86ef840 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Utopia\App; use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; diff --git a/src/Appwrite/Event/Delete.php b/src/Appwrite/Event/Delete.php index d93dffaf44..57300feb72 100644 --- a/src/Appwrite/Event/Delete.php +++ b/src/Appwrite/Event/Delete.php @@ -128,8 +128,6 @@ class Delete extends Event { $client = new Client($this->queue, $this->connection); - $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; - return $client->enqueue([ 'project' => $this->project, 'type' => $this->type, diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 07f496788f..46b430d122 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -50,6 +50,7 @@ class Event protected array $context = []; protected ?Document $project = null; protected ?Document $user = null; + protected bool $paused = false; /** * @param Connection $connection @@ -264,6 +265,9 @@ class Event */ public function trigger(): string|bool { + if ($this->paused) { + return false; + } $client = new Client($this->queue, $this->connection); @@ -472,4 +476,22 @@ class Event */ return \array_values($events); } + + /** + * Get the value of paused + */ + public function isPaused(): bool + { + return $this->paused; + } + + /** + * Set the value of paused + */ + public function setPaused(bool $paused): self + { + $this->paused = $paused; + + return $this; + } } diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index f211ec31be..11c9e980ed 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -188,6 +188,10 @@ class Func extends Event */ public function trigger(): string|bool { + if ($this->paused) { + return false; + } + $client = new Client($this->queue, $this->connection); $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; diff --git a/src/Appwrite/Event/Mail.php b/src/Appwrite/Event/Mail.php index 393df3d979..c2de8023b0 100644 --- a/src/Appwrite/Event/Mail.php +++ b/src/Appwrite/Event/Mail.php @@ -2,7 +2,6 @@ namespace Appwrite\Event; -use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; diff --git a/src/Appwrite/Event/Migration.php b/src/Appwrite/Event/Migration.php index 93126cb358..478291829b 100644 --- a/src/Appwrite/Event/Migration.php +++ b/src/Appwrite/Event/Migration.php @@ -2,9 +2,6 @@ namespace Appwrite\Event; -use DateTime; -use Resque; -use ResqueScheduler; use Utopia\Database\Document; use Utopia\Queue\Client; use Utopia\Queue\Connection; diff --git a/src/Appwrite/Event/Phone.php b/src/Appwrite/Event/Phone.php index 9f27345a95..45f193a540 100644 --- a/src/Appwrite/Event/Phone.php +++ b/src/Appwrite/Event/Phone.php @@ -75,8 +75,6 @@ class Phone extends Event { $client = new Client($this->queue, $this->connection); - $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; - return $client->enqueue([ 'project' => $this->project, 'user' => $this->user, From 7806cf1b55a9b799ae4aaae459b47560ac216886 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 23:29:25 +0400 Subject: [PATCH 116/144] chore: cleanup obsolete CLI tasks --- composer.lock | 2 +- src/Appwrite/Platform/Services/Tasks.php | 10 - .../Platform/Tasks/CalcUsersStats.php | 176 ------------------ .../Platform/Tasks/ClearCardCache.php | 62 ------ .../Tasks/PatchCreateMissingSchedules.php | 97 ---------- .../Tasks/PatchDeleteProjectCollections.php | 129 ------------- .../PatchDeleteScheduleUpdatedAtAttribute.php | 74 -------- src/Appwrite/Platform/Workers/Messaging.php | 3 + 8 files changed, 4 insertions(+), 549 deletions(-) delete mode 100644 src/Appwrite/Platform/Tasks/CalcUsersStats.php delete mode 100644 src/Appwrite/Platform/Tasks/ClearCardCache.php delete mode 100644 src/Appwrite/Platform/Tasks/PatchCreateMissingSchedules.php delete mode 100644 src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php delete mode 100644 src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php diff --git a/composer.lock b/composer.lock index 7ccc6df4df..4634780720 100644 --- a/composer.lock +++ b/composer.lock @@ -6019,5 +6019,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index bc8d1bbc72..00779084d4 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -8,20 +8,15 @@ use Appwrite\Platform\Tasks\Install; use Appwrite\Platform\Tasks\Maintenance; use Appwrite\Platform\Tasks\Migrate; use Appwrite\Platform\Tasks\Schedule; -use Appwrite\Platform\Tasks\PatchCreateMissingSchedules; use Appwrite\Platform\Tasks\SDKs; use Appwrite\Platform\Tasks\Specs; use Appwrite\Platform\Tasks\SSL; use Appwrite\Platform\Tasks\Hamster; -use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute; -use Appwrite\Platform\Tasks\ClearCardCache; use Appwrite\Platform\Tasks\Usage; use Appwrite\Platform\Tasks\Vars; use Appwrite\Platform\Tasks\Version; use Appwrite\Platform\Tasks\VolumeSync; -use Appwrite\Platform\Tasks\CalcUsersStats; use Appwrite\Platform\Tasks\CalcTierStats; -use Appwrite\Platform\Tasks\PatchDeleteProjectCollections; use Appwrite\Platform\Tasks\Upgrade; class Tasks extends Service @@ -39,17 +34,12 @@ class Tasks extends Service ->addAction(Install::getName(), new Install()) ->addAction(Upgrade::getName(), new Upgrade()) ->addAction(Maintenance::getName(), new Maintenance()) - ->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules()) - ->addAction(ClearCardCache::getName(), new ClearCardCache()) - ->addAction(PatchDeleteScheduleUpdatedAtAttribute::getName(), new PatchDeleteScheduleUpdatedAtAttribute()) ->addAction(Schedule::getName(), new Schedule()) ->addAction(Migrate::getName(), new Migrate()) ->addAction(SDKs::getName(), new SDKs()) ->addAction(VolumeSync::getName(), new VolumeSync()) ->addAction(Specs::getName(), new Specs()) - ->addAction(CalcUsersStats::getName(), new CalcUsersStats()) ->addAction(CalcTierStats::getName(), new CalcTierStats()) - ->addAction(PatchDeleteProjectCollections::getName(), new PatchDeleteProjectCollections()) ; } } diff --git a/src/Appwrite/Platform/Tasks/CalcUsersStats.php b/src/Appwrite/Platform/Tasks/CalcUsersStats.php deleted file mode 100644 index 6310fe17b4..0000000000 --- a/src/Appwrite/Platform/Tasks/CalcUsersStats.php +++ /dev/null @@ -1,176 +0,0 @@ -desc('Get stats for projects') - ->inject('pools') - ->inject('cache') - ->inject('dbForConsole') - ->inject('register') - ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) { - $this->action($pools, $cache, $dbForConsole, $register); - }); - } - - public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void - { - //docker compose exec -t appwrite calc-users-stats - - Console::title('Cloud Users calculation V1'); - Console::success(APP_NAME . ' cloud Users calculation has started'); - - /* Initialise new Utopia app */ - $app = new App('UTC'); - $console = $app->getResource('console'); - - /** CSV stuff */ - $this->date = date('Y-m-d'); - $this->path = "{$this->directory}/users_stats_{$this->date}.csv"; - $csv = Writer::createFromPath($this->path, 'w'); - $csv->insertOne($this->columns); - - /** Database connections */ - $totalProjects = $dbForConsole->count('projects'); - Console::success("Found a total of: {$totalProjects} projects"); - - $projects = [$console]; - $count = 0; - $limit = 30; - $sum = 30; - $offset = 0; - while (!empty($projects)) { - foreach ($projects as $project) { - - /** - * Skip user projects with id 'console' - */ - if ($project->getId() === 'console') { - continue; - } - - Console::info("Getting stats for {$project->getId()}"); - - try { - $db = $project->getAttribute('database'); - $adapter = $pools - ->get($db) - ->pop() - ->getResource(); - - $dbForProject = new Database($adapter, $cache); - $dbForProject->setDefaultDatabase('appwrite'); - $dbForProject->setNamespace('_' . $project->getInternalId()); - - /** Get Project ID */ - $stats['Project ID'] = $project->getId(); - - /** Get Project Name */ - $stats['Project Name'] = $project->getAttribute('name'); - - - /** Get Team Name and Id */ - $teamId = $project->getAttribute('teamId', null); - $teamName = null; - if ($teamId) { - $team = $dbForConsole->getDocument('teams', $teamId); - $teamName = $team->getAttribute('name'); - } - - $stats['Team ID'] = $teamId; - $stats['Team name'] = $teamName; - - /** Get Total Users */ - $stats['users'] = $dbForProject->count('users', []); - - $csv->insertOne(array_values($stats)); - } catch (\Throwable $th) { - Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage()); - } finally { - $pools - ->get($db) - ->reclaim(); - } - } - - $sum = \count($projects); - - $projects = $dbForConsole->find('projects', [ - Query::limit($limit), - Query::offset($offset), - ]); - - $offset = $offset + $limit; - $count = $count + $sum; - } - Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects...'); - $pools - ->get('console') - ->reclaim(); - - /** @var PHPMailer $mail */ - $mail = $register->get('smtp'); - - $mail->clearAddresses(); - $mail->clearAllRecipients(); - $mail->clearReplyTos(); - $mail->clearAttachments(); - $mail->clearBCCs(); - $mail->clearCCs(); - - try { - /** Addresses */ - $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster'); - $recipients = explode(',', App::getEnv('_APP_USERS_STATS_RECIPIENTS', '')); - - foreach ($recipients as $recipient) { - $mail->addAddress($recipient); - } - - /** Attachments */ - $mail->addAttachment($this->path); - - /** Content */ - $mail->Subject = "Cloud Report for {$this->date}"; - $mail->Body = "Please find the daily cloud report atttached"; - $mail->send(); - Console::success('Email has been sent!'); - } catch (Exception $e) { - Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}"); - } - } -} diff --git a/src/Appwrite/Platform/Tasks/ClearCardCache.php b/src/Appwrite/Platform/Tasks/ClearCardCache.php deleted file mode 100644 index d3153b995c..0000000000 --- a/src/Appwrite/Platform/Tasks/ClearCardCache.php +++ /dev/null @@ -1,62 +0,0 @@ -desc('Deletes card cache for specific user') - ->param('userId', '', new UID(), 'User UID.', false) - ->inject('dbForConsole') - ->callback(fn (string $userId, Database $dbForConsole) => $this->action($userId, $dbForConsole)); - } - - public function action(string $userId, Database $dbForConsole): void - { - Authorization::disable(); - Authorization::setDefaultStatus(false); - - Console::title('ClearCardCache V1'); - Console::success(APP_NAME . ' ClearCardCache v1 has started'); - $resources = ['card/' . $userId, 'card-back/' . $userId, 'card-og/' . $userId]; - - $caches = Authorization::skip(fn () => $dbForConsole->find('cache', [ - Query::equal('resource', $resources), - Query::limit(100) - ])); - - $count = \count($caches); - Console::info("Going to delete {$count} cache records in 10 seconds..."); - \sleep(10); - - foreach ($caches as $cache) { - $key = $cache->getId(); - - $cacheFolder = new Cache( - new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-console') - ); - - $cacheFolder->purge($key); - - Authorization::skip(fn () => $dbForConsole->deleteDocument('cache', $cache->getId())); - } - - Console::success(APP_NAME . ' ClearCardCache v1 has finished'); - } -} diff --git a/src/Appwrite/Platform/Tasks/PatchCreateMissingSchedules.php b/src/Appwrite/Platform/Tasks/PatchCreateMissingSchedules.php deleted file mode 100644 index 74ef644498..0000000000 --- a/src/Appwrite/Platform/Tasks/PatchCreateMissingSchedules.php +++ /dev/null @@ -1,97 +0,0 @@ -desc('Ensure every function has a schedule') - ->inject('dbForConsole') - ->inject('getProjectDB') - ->callback(fn (Database $dbForConsole, callable $getProjectDB) => $this->action($dbForConsole, $getProjectDB)); - } - - /** - * Iterate over every function on every project to make sure there is a schedule. If not, recreate the schedule. - */ - public function action(Database $dbForConsole, callable $getProjectDB): void - { - Authorization::disable(); - Authorization::setDefaultStatus(false); - - Console::title('PatchCreateMissingSchedules V1'); - Console::success(APP_NAME . ' PatchCreateMissingSchedules v1 has started'); - - $limit = 100; - $projectCursor = null; - while (true) { - $projectsQueries = [Query::limit($limit)]; - if ($projectCursor !== null) { - $projectsQueries[] = Query::cursorAfter($projectCursor); - } - $projects = $dbForConsole->find('projects', $projectsQueries); - - if (count($projects) === 0) { - break; - } - - foreach ($projects as $project) { - Console::log("Checking Project " . $project->getAttribute('name') . " (" . $project->getId() . ")"); - $dbForProject = $getProjectDB($project); - $functionCursor = null; - - while (true) { - $functionsQueries = [Query::limit($limit)]; - if ($functionCursor !== null) { - $functionsQueries[] = Query::cursorAfter($functionCursor); - } - $functions = $dbForProject->find('functions', $functionsQueries); - if (count($functions) === 0) { - break; - } - - foreach ($functions as $function) { - $scheduleId = $function->getAttribute('scheduleId'); - $schedule = $dbForConsole->getDocument('schedules', $scheduleId); - - if ($schedule->isEmpty()) { - $functionId = $function->getId(); - $schedule = $dbForConsole->createDocument('schedules', new Document([ - '$id' => ID::custom($scheduleId), - 'region' => $project->getAttribute('region', 'default'), - 'resourceType' => 'function', - 'resourceId' => $functionId, - 'resourceUpdatedAt' => DateTime::now(), - 'projectId' => $project->getId(), - 'schedule' => $function->getAttribute('schedule'), - 'active' => !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')), - ])); - - Console::success('Recreated schedule for function ' . $functionId); - } - } - - $functionCursor = $functions[array_key_last($functions)]; - } - } - - $projectCursor = $projects[array_key_last($projects)]; - } - } -} diff --git a/src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php b/src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php deleted file mode 100644 index a909e68595..0000000000 --- a/src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php +++ /dev/null @@ -1,129 +0,0 @@ -desc('Delete unnecessary project collections') - ->param('offset', 0, new Numeric(), 'Resume deletion from param pos', true) - ->inject('pools') - ->inject('cache') - ->inject('dbForConsole') - ->callback(function (int $offset, Group $pools, Cache $cache, Database $dbForConsole) { - $this->action($offset, $pools, $cache, $dbForConsole); - }); - } - - public function action(int $offset, Group $pools, Cache $cache, Database $dbForConsole): void - { - //docker compose exec -t appwrite patch-delete-project-collections - - Console::title('Delete project collections V1'); - Console::success(APP_NAME . ' delete project collections has started'); - - /* Initialise new Utopia app */ - $app = new App('UTC'); - $console = $app->getResource('console'); - - /** Database connections */ - $totalProjects = $dbForConsole->count('projects'); - Console::success("Found a total of: {$totalProjects} projects"); - - $projects = [$console]; - $count = 0; - $limit = 50; - $sum = 50; - $offset = $offset; - while (!empty($projects)) { - foreach ($projects as $project) { - - /** - * Skip user projects with id 'console' - */ - if ($project->getId() === 'console') { - continue; - } - - Console::info("Deleting collections for {$project->getId()}"); - - try { - $db = $project->getAttribute('database'); - $adapter = $pools - ->get($db) - ->pop() - ->getResource(); - - $dbForProject = new Database($adapter, $cache); - $dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); - $dbForProject->setNamespace('_' . $project->getInternalId()); - - foreach ($this->names as $name) { - if (empty($name)) { - continue; - } - if ($dbForProject->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) { - if ($dbForProject->deleteCollection($name)) { - Console::log('Deleted ' . $name); - } else { - Console::error('Failed to delete ' . $name); - } - } - } - } catch (\Throwable $th) { - Console::error('Failed on project ("' . $project->getId() . '") version with error: ' . $th->getMessage()); - } finally { - $pools - ->get($db) - ->reclaim(); - } - } - - $sum = \count($projects); - - $projects = $dbForConsole->find('projects', [ - Query::limit($limit), - Query::offset($offset), - ]); - - if (!empty($projects)) { - Console::log('Querying..... offset=' . $offset . ' , limit=' . $limit . ', count=' . $count); - } - - $offset = $offset + $limit; - $count = $count + $sum; - } - Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects...'); - $pools - ->get('console') - ->reclaim(); - } -} diff --git a/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php b/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php deleted file mode 100644 index 95a7c4ffe1..0000000000 --- a/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php +++ /dev/null @@ -1,74 +0,0 @@ -desc('Ensure function collections do not have scheduleUpdatedAt attribute') - ->inject('pools') - ->inject('dbForConsole') - ->inject('getProjectDB') - ->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); - } - - /** - * Iterate over every function on every project to make sure there is a schedule. If not, recreate the schedule. - */ - public function action(Group $pools, Database $dbForConsole, callable $getProjectDB): void - { - Authorization::disable(); - Authorization::setDefaultStatus(false); - - Console::title('PatchDeleteScheduleUpdatedAtAttribute V1'); - Console::success(APP_NAME . ' PatchDeleteScheduleUpdatedAtAttribute v1 has started'); - - $limit = 100; - $projectCursor = null; - while (true) { - $projectsQueries = [Query::limit($limit)]; - if ($projectCursor !== null) { - $projectsQueries[] = Query::cursorAfter($projectCursor); - } - $projects = $dbForConsole->find('projects', $projectsQueries); - - if (count($projects) === 0) { - break; - } - - foreach ($projects as $project) { - Console::log("Checking Project " . $project->getAttribute('name') . " (" . $project->getId() . ")"); - $dbForProject = $getProjectDB($project); - - try { - /** - * Delete 'scheduleUpdatedAt' attribute - */ - $dbForProject->deleteAttribute('functions', 'scheduleUpdatedAt'); - $dbForProject->deleteCachedCollection('functions'); - Console::success("'scheduleUpdatedAt' deleted."); - } catch (\Throwable $th) { - Console::warning("'scheduleUpdatedAt' errored: {$th->getMessage()}"); - } - - $pools->reclaim(); - } - - $projectCursor = $projects[array_key_last($projects)]; - } - } -} diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 76b86e4f0c..ecdc48376a 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -33,6 +33,9 @@ class Messaging extends Action */ public function __construct() { + + throw new Exception('Exception from the Messaging Worker'); + $this->provider = App::getEnv('_APP_SMS_PROVIDER', ''); if (!empty($this->provider)) { $this->dsn = new DSN($this->provider); From 617ded14aa12f8cc98c366d82fb437167ad8a2f9 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 23:30:11 +0400 Subject: [PATCH 117/144] chore: cleanup obsolete CLI tasks --- src/Appwrite/Platform/Workers/Messaging.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index ecdc48376a..76b86e4f0c 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -33,9 +33,6 @@ class Messaging extends Action */ public function __construct() { - - throw new Exception('Exception from the Messaging Worker'); - $this->provider = App::getEnv('_APP_SMS_PROVIDER', ''); if (!empty($this->provider)) { $this->dsn = new DSN($this->provider); From d1ea0c2244565fe93bff476600dc5acd77fdb412 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 23:38:03 +0400 Subject: [PATCH 118/144] chore: cleanup obsolete CLI tasks --- composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index e45d242295..7ccc6df4df 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": "5060c891e467051fedd25f3853cd37b0", + "content-hash": "e34e098996ca4769e2a956b423522e91", "packages": [ { "name": "adhocore/jwt", @@ -2516,16 +2516,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.1.1", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", "shasum": "" }, "require": { @@ -2534,8 +2534,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" }, "type": "library", "autoload": { @@ -2558,9 +2558,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" }, - "time": "2023-02-07T05:42:46+00:00" + "time": "2023-09-14T20:48:42+00:00" }, { "name": "utopia-php/migration", @@ -6019,5 +6019,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.2.0" } From 8afc29c5edbe1f8e05012b6d89e19d4aadb0572a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 23:49:10 +0400 Subject: [PATCH 119/144] chore: cleanup obsolete CLI tasks --- composer.lock | 6023 ------------------------------------------------- 1 file changed, 6023 deletions(-) delete mode 100644 composer.lock diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 7ccc6df4df..0000000000 --- a/composer.lock +++ /dev/null @@ -1,6023 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "e34e098996ca4769e2a956b423522e91", - "packages": [ - { - "name": "adhocore/jwt", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ahc\\Jwt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", - "keywords": [ - "auth", - "json-web-token", - "jwt", - "jwt-auth", - "jwt-php", - "token" - ], - "support": { - "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - } - ], - "time": "2021-02-20T09:56:44+00:00" - }, - { - "name": "appwrite/appwrite", - "version": "10.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", - "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\": "src/Appwrite" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", - "support": { - "email": "team@appwrite.io", - "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", - "url": "https://appwrite.io/support" - }, - "time": "2023-09-07T23:28:31+00:00" - }, - { - "name": "appwrite/php-clamav", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/php-clamav.git", - "reference": "f3897169f5c1f365312238a516ae9465f804634f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", - "reference": "f3897169f5c1f365312238a516ae9465f804634f", - "shasum": "" - }, - "require": { - "ext-sockets": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\ClamAV\\": "src/ClamAV" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "ClamAV network and pipe client for PHP", - "keywords": [ - "anti virus", - "appwrite", - "clamav", - "php" - ], - "support": { - "issues": "https://github.com/appwrite/php-clamav/issues", - "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" - }, - "time": "2023-02-24T09:50:42+00:00" - }, - { - "name": "appwrite/php-runtimes", - "version": "0.13.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610" - }, - "require": { - "php": ">=8.0", - "utopia-php/system": "0.7.*" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\Runtimes\\": "src/Runtimes" - } - }, - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", - "keywords": [ - "appwrite", - "php", - "runtimes" - ], - "time": "2023-10-16T15:39:53+00:00" - }, - { - "name": "chillerlan/php-qrcode", - "version": "4.3.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-qrcode.git", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", - "shasum": "" - }, - "require": { - "chillerlan/php-settings-container": "^2.1.4", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5", - "setasign/fpdf": "^1.8.2" - }, - "suggest": { - "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", - "setasign/fpdf": "Required to use the QR FPDF output." - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\QRCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kazuhiko Arase", - "homepage": "https://github.com/kazuhikoarase" - }, - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - }, - { - "name": "Contributors", - "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" - } - ], - "description": "A QR code generator. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-qrcode", - "keywords": [ - "phpqrcode", - "qr", - "qr code", - "qrcode", - "qrcode-generator" - ], - "support": { - "issues": "https://github.com/chillerlan/php-qrcode/issues", - "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-25T09:12:45+00:00" - }, - { - "name": "chillerlan/php-settings-container", - "version": "2.1.4", - "source": { - "type": "git", - "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phan/phan": "^5.3", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "chillerlan\\Settings\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Smiley", - "email": "smiley@chillerlan.net", - "homepage": "https://github.com/codemasher" - } - ], - "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", - "homepage": "https://github.com/chillerlan/php-settings-container", - "keywords": [ - "PHP7", - "Settings", - "configuration", - "container", - "helper" - ], - "support": { - "issues": "https://github.com/chillerlan/php-settings-container/issues", - "source": "https://github.com/chillerlan/php-settings-container" - }, - "funding": [ - { - "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", - "type": "custom" - }, - { - "url": "https://ko-fi.com/codemasher", - "type": "ko_fi" - } - ], - "time": "2022-07-05T22:32:14+00:00" - }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.3.2", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-09-10T18:51:20+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:20:53+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2023-08-03T15:11:55+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.6.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-08-27T10:13:57+00:00" - }, - { - "name": "influxdb/influxdb-php", - "version": "1.15.2", - "source": { - "type": "git", - "url": "https://github.com/influxdata/influxdb-php.git", - "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", - "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^6.0|^7.0", - "php": "^5.5 || ^7.0 || ^8.0" - }, - "require-dev": { - "dms/phpunit-arraysubset-asserts": "^0.2.1", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-curl": "Curl extension, needed for Curl driver", - "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." - }, - "type": "library", - "autoload": { - "psr-4": { - "InfluxDB\\": "src/InfluxDB" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Stephen Hoogendijk", - "email": "stephen@tca0.nl" - }, - { - "name": "Daniel Martinez", - "email": "danimartcas@hotmail.com" - }, - { - "name": "Gianluca Arbezzano", - "email": "gianarb92@gmail.com" - } - ], - "description": "InfluxDB client library for PHP", - "keywords": [ - "client", - "influxdata", - "influxdb", - "influxdb class", - "influxdb client", - "influxdb library", - "time series" - ], - "support": { - "issues": "https://github.com/influxdata/influxdb-php/issues", - "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" - }, - "abandoned": true, - "time": "2020-12-26T17:45:17+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^0.12.66", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" - }, - "time": "2021-10-08T21:21:46+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "league/csv", - "version": "9.7.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/csv.git", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", - "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "ext-curl": "*", - "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^2.16", - "phpstan/phpstan": "^0.12.0", - "phpstan/phpstan-phpunit": "^0.12.0", - "phpstan/phpstan-strict-rules": "^0.12.0", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", - "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "League\\Csv\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://github.com/nyamsprod/", - "role": "Developer" - } - ], - "description": "CSV data manipulation made easy in PHP", - "homepage": "http://csv.thephpleague.com", - "keywords": [ - "convert", - "csv", - "export", - "filter", - "import", - "read", - "transform", - "write" - ], - "support": { - "docs": "https://csv.thephpleague.com", - "issues": "https://github.com/thephpleague/csv/issues", - "rss": "https://github.com/thephpleague/csv/releases.atom", - "source": "https://github.com/thephpleague/csv" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2021-04-17T16:32:08+00:00" - }, - { - "name": "matomo/device-detector", - "version": "6.1.6", - "source": { - "type": "git", - "url": "https://github.com/matomo-org/device-detector.git", - "reference": "5cbea85106e561c7138d03603eb6e05128480409" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", - "reference": "5cbea85106e561c7138d03603eb6e05128480409", - "shasum": "" - }, - "require": { - "mustangostang/spyc": "*", - "php": "^7.2|^8.0" - }, - "replace": { - "piwik/device-detector": "self.version" - }, - "require-dev": { - "matthiasmullie/scrapbook": "^1.4.7", - "mayflower/mo4-coding-standard": "^v8.0.0", - "phpstan/phpstan": "^0.12.52", - "phpunit/phpunit": "^8.5.8", - "psr/cache": "^1.0.1", - "psr/simple-cache": "^1.0.1", - "symfony/yaml": "^5.1.7" - }, - "suggest": { - "doctrine/cache": "Can directly be used for caching purpose", - "ext-yaml": "Necessary for using the Pecl YAML parser" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeviceDetector\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "The Matomo Team", - "email": "hello@matomo.org", - "homepage": "https://matomo.org/team/" - } - ], - "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", - "homepage": "https://matomo.org", - "keywords": [ - "devicedetection", - "parser", - "useragent" - ], - "support": { - "forum": "https://forum.matomo.org/", - "issues": "https://github.com/matomo-org/device-detector/issues", - "source": "https://github.com/matomo-org/matomo", - "wiki": "https://dev.matomo.org/" - }, - "time": "2023-10-02T10:01:54+00:00" - }, - { - "name": "mongodb/mongodb", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", - "shasum": "" - }, - "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.11.0", - "jean85/pretty-package-versions": "^1.2 || ^2.0.1", - "php": "^7.1 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "squizlabs/php_codesniffer": "^3.6", - "symfony/phpunit-bridge": "^5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" - }, - "time": "2021-10-20T22:22:37+00:00" - }, - { - "name": "mustangostang/spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "git@github.com:mustangostang/spyc.git", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.8.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", - "doctrine/annotations": "^1.2.6 || ^1.13.3", - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.1", - "yoast/phpunit-polyfills": "^1.0.4" - }, - "suggest": { - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "ext-openssl": "Needed for secure SMTP sending and DKIM signing", - "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2023-03-06T14:43:22+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" - }, - "time": "2023-04-10T20:10:41+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, - { - "name": "slickdeals/statsd", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/Slickdeals/statsd-php.git", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", - "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", - "shasum": "" - }, - "require": { - "php": ">= 7.3 || ^8" - }, - "replace": { - "domnikl/statsd": "self.version" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://github.com/Slickdeals/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/Slickdeals/statsd-php/issues", - "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" - }, - "time": "2021-06-04T20:33:46+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-23T14:45:45+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "utopia-php/abuse", - "version": "0.31.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/abuse.git", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Abuse\\": "src/Abuse" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple abuse library to manage application usage limits", - "keywords": [ - "Abuse", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.31.1" - }, - "time": "2023-08-29T11:07:46+00:00" - }, - { - "name": "utopia-php/analytics", - "version": "0.10.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "dev-main", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Analytics\\": "src/Analytics" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to track events & users.", - "keywords": [ - "analytics", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" - }, - "time": "2023-03-22T12:01:09+00:00" - }, - { - "name": "utopia-php/audit", - "version": "0.33.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/audit.git", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/database": "0.43.*" - }, - "require-dev": { - "laravel/pint": "1.5.*", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Audit\\": "src/Audit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple audit library to manage application users logs", - "keywords": [ - "Audit", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.33.1" - }, - "time": "2023-08-29T11:07:40+00:00" - }, - { - "name": "utopia-php/cache", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cache.git", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", - "reference": "212e66100a1f32e674fca5d9bc317cc998303089", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-memcached": "*", - "ext-redis": "*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Cache\\": "src/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple cache library to manage application cache storing, loading and purging", - "keywords": [ - "cache", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.8.0" - }, - "time": "2022-10-16T16:48:09+00:00" - }, - { - "name": "utopia-php/cli", - "version": "0.15.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\CLI\\": "src/CLI" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple CLI library to manage command line applications", - "keywords": [ - "cli", - "command line", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" - }, - "time": "2023-03-01T05:55:14+00:00" - }, - { - "name": "utopia-php/config", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/config.git", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Config\\": "src/Config" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Config library to managing application config variables", - "keywords": [ - "config", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/config/issues", - "source": "https://github.com/utopia-php/config/tree/0.2.2" - }, - "time": "2020-10-24T09:49:09+00:00" - }, - { - "name": "utopia-php/database", - "version": "0.43.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", - "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.3.*" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.4.*", - "pcov/clobber": "^2.0", - "phpstan/phpstan": "1.10.*", - "phpunit/phpunit": "^9.4", - "rregeer/phpunit-coverage-check": "^0.3.1", - "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Database\\": "src/Database" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to manage application persistence using multiple database adapters", - "keywords": [ - "database", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.43.5" - }, - "time": "2023-10-06T06:49:47+00:00" - }, - { - "name": "utopia-php/domains", - "version": "0.3.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Domains\\": "src/Domains" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess Cope", - "email": "wess@appwrite.io" - } - ], - "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "domains", - "framework", - "icann", - "php", - "public suffix", - "tld", - "tld extract", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.3.2" - }, - "time": "2023-07-19T16:39:24+00:00" - }, - { - "name": "utopia-php/dsn", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dsn.git", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DSN\\": "src/DSN" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library for parsing and managing Data Source Names ( DSNs )", - "keywords": [ - "dsn", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.1.0" - }, - "time": "2022-10-26T10:06:20+00:00" - }, - { - "name": "utopia-php/framework", - "version": "0.31.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/framework.git", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", - "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP framework", - "keywords": [ - "framework", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.31.0" - }, - "time": "2023-08-30T16:10:04+00:00" - }, - { - "name": "utopia-php/image", - "version": "0.5.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/image.git", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", - "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", - "shasum": "" - }, - "require": { - "ext-imagick": "*", - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.13.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Image\\": "src/Image" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple Image manipulation library", - "keywords": [ - "framework", - "image", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.5.4" - }, - "time": "2022-05-11T12:30:41+00:00" - }, - { - "name": "utopia-php/locale", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/locale.git", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Locale\\": "src/Locale" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple locale library to manage application translations", - "keywords": [ - "framework", - "locale", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/locale/issues", - "source": "https://github.com/utopia-php/locale/tree/0.4.0" - }, - "time": "2021-07-24T11:35:55+00:00" - }, - { - "name": "utopia-php/logger", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" - }, - "time": "2023-02-10T15:52:50+00:00" - }, - { - "name": "utopia-php/messaging", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Messaging\\": "src/Utopia/Messaging" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP messaging library", - "keywords": [ - "library", - "messaging", - "php", - "upf", - "utopia", - "utopia-php" - ], - "support": { - "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" - }, - "time": "2023-09-14T20:48:42+00:00" - }, - { - "name": "utopia-php/migration", - "version": "0.3.5", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/migration.git", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "shasum": "" - }, - "require": { - "appwrite/appwrite": "10.0.*", - "php": "8.*", - "utopia-php/cli": "0.*" - }, - "require-dev": { - "laravel/pint": "1.*", - "phpunit/phpunit": "9.*", - "vlucas/phpdotenv": "5.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Migration\\": "src/Migration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to migrate resources between services.", - "keywords": [ - "framework", - "migration", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.3.5" - }, - "time": "2023-09-25T16:51:47+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.3.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", - "shasum": "" - }, - "require": { - "ext-mongodb": "*", - "mongodb/mongodb": "1.10.0", - "php": ">=8.0" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4", - "swoole/ide-helper": "4.8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.3.1" - }, - "time": "2023-09-01T17:25:28+00:00" - }, - { - "name": "utopia-php/orchestration", - "version": "0.9.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Orchestration\\": "src/Orchestration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lite & fast micro PHP abstraction library for container orchestration", - "keywords": [ - "docker", - "framework", - "kubernetes", - "orchestration", - "php", - "swarm", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" - }, - "time": "2023-03-17T15:05:06+00:00" - }, - { - "name": "utopia-php/platform", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/229a7b1fa1f39afd1532f7a515326a6afc222a26", - "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-redis": "*", - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Platform\\": "src/Platform" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Light and Fast Platform Library", - "keywords": [ - "framework", - "php", - "platform", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.5.0" - }, - "time": "2023-10-16T20:28:49+00:00" - }, - { - "name": "utopia-php/pools", - "version": "0.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" - }, - "time": "2022-11-22T07:55:45+00:00" - }, - { - "name": "utopia-php/preloader", - "version": "0.2.4", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/preloader.git", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", - "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Preloader\\": "src/Preloader" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "team@appwrite.io" - } - ], - "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", - "keywords": [ - "framework", - "php", - "preload", - "preloader", - "preloading", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/preloader/issues", - "source": "https://github.com/utopia-php/preloader/tree/0.2.4" - }, - "time": "2020-10-24T07:04:59+00:00" - }, - { - "name": "utopia-php/queue", - "version": "0.5.3", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/queue.git", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.8.8", - "workerman/workerman": "^4.0" - }, - "suggest": { - "ext-swoole": "Needed to support Swoole.", - "workerman/workerman": "Needed to support Workerman." - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Queue\\": "src/Queue" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A powerful task queue.", - "keywords": [ - "Tasks", - "framework", - "php", - "queue", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.3" - }, - "time": "2023-05-24T19:06:04+00:00" - }, - { - "name": "utopia-php/registry", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/registry.git", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", - "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Registry\\": "src/Registry" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "A simple dependency management library for PHP", - "keywords": [ - "dependency management", - "di", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/registry/issues", - "source": "https://github.com/utopia-php/registry/tree/0.5.0" - }, - "time": "2021-03-10T10:45:22+00:00" - }, - { - "name": "utopia-php/storage", - "version": "0.17.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", - "shasum": "" - }, - "require": { - "ext-brotli": "*", - "ext-fileinfo": "*", - "ext-lz4": "*", - "ext-snappy": "*", - "ext-xz": "*", - "ext-zlib": "*", - "ext-zstd": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Storage\\": "src/Storage" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple Storage library to manage application storage", - "keywords": [ - "framework", - "php", - "storage", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.17.0" - }, - "time": "2023-08-21T11:28:36+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.*.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "4.8.3", - "vimeo/psalm": "4.15.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.5.0" - }, - "time": "2022-10-19T22:19:07+00:00" - }, - { - "name": "utopia-php/system", - "version": "0.7.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/system.git", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", - "shasum": "" - }, - "require": { - "laravel/pint": "1.2.*", - "php": ">=8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\System\\": "src/System" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple library for obtaining information about the host's system.", - "keywords": [ - "framework", - "php", - "system", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.7.1" - }, - "time": "2023-08-30T09:14:37+00:00" - }, - { - "name": "utopia-php/vcs", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/vcs.git", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "shasum": "" - }, - "require": { - "adhocore/jwt": "^1.1", - "php": ">=8.0", - "utopia-php/cache": "^0.8.0", - "utopia-php/framework": "0.31.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\VCS\\": "src/VCS", - "Utopia\\Detector\\": "src/Detector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", - "keywords": [ - "framework", - "php", - "utopia", - "vcs" - ], - "support": { - "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.5.0" - }, - "time": "2023-09-13T19:05:52+00:00" - }, - { - "name": "utopia-php/websocket", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", - "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", - "workerman/workerman": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\WebSocket\\": "src/WebSocket" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], - "description": "A simple abstraction for WebSocket servers.", - "keywords": [ - "framework", - "php", - "upf", - "utopia", - "websocket" - ], - "support": { - "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" - }, - "time": "2021-12-20T10:50:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.10", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" - }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-07-05T14:23:37+00:00" - } - ], - "packages-dev": [ - { - "name": "appwrite/sdk-generator", - "version": "0.35.2", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", - "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "matthiasmullie/minify": "^1.3.68", - "php": ">=8.0", - "twig/twig": "^3.4.1" - }, - "require-dev": { - "brianium/paratest": "^6.4", - "phpunit/phpunit": "^9.5.21", - "squizlabs/php_codesniffer": "^3.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "support": { - "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" - }, - "time": "2023-09-14T14:59:50+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" - }, - "time": "2023-09-27T20:04:15+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "matthiasmullie/minify", - "version": "1.3.71", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.1", - "php": ">=5.3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": ">=2.0", - "matthiasmullie/scrapbook": ">=1.3", - "phpunit/phpunit": ">=4.8", - "squizlabs/php_codesniffer": ">=3.0" - }, - "suggest": { - "psr/cache-implementation": "Cache implementation to use with Minify::cache" - }, - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "https://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", - "homepage": "https://github.com/matthiasmullie/minify", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ], - "support": { - "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" - }, - "funding": [ - { - "url": "https://github.com/matthiasmullie", - "type": "github" - } - ], - "time": "2023-04-25T20:33:03+00:00" - }, - { - "name": "matthiasmullie/path-converter", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MatthiasMullie\\PathConverter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "pathconverter@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "Relative path converter", - "homepage": "http://github.com/matthiasmullie/path-converter", - "keywords": [ - "converter", - "path", - "paths", - "relative" - ], - "support": { - "issues": "https://github.com/matthiasmullie/path-converter/issues", - "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" - }, - "time": "2019-02-05T23:41:09+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" - }, - "time": "2021-10-19T17:43:47+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.7.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" - }, - "time": "2023-08-12T11:01:26+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.17.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" - }, - "time": "2023-02-02T15:41:36+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.24.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bcad8d995980440892759db0c32acae7c8e79442" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", - "reference": "bcad8d995980440892759db0c32acae7c8e79442", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" - }, - "time": "2023-09-26T12:28:12+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.29", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-19T04:57:46+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-01T12:37:26+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-07T05:35:17+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:03:51+00:00" - }, - { - "name": "sebastian/exporter", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T06:03:37+00:00" - }, - { - "name": "sebastian/global-state", - "version": "5.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-28T06:42:11+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:12:34+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:14:26+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "4.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" - }, - { - "name": "sebastian/type", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:39:44+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - }, - { - "name": "swoole/ide-helper", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/swoole/ide-helper.git", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "shasum": "" - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Team Swoole", - "email": "team@swoole.com" - } - ], - "description": "IDE help files for Swoole.", - "support": { - "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/5.0.2" - }, - "time": "2023-03-20T06:05:55+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/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.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-28T09:04:16+00:00" - }, - { - "name": "textalk/websocket", - "version": "1.5.7", - "source": { - "type": "git", - "url": "https://github.com/Textalk/websocket-php.git", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", - "shasum": "" - }, - "require": { - "php": "^7.2 | ^8.0", - "psr/log": "^1 | ^2 | ^3" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "WebSocket\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Fredrik Liljegren" - }, - { - "name": "Sören Jensen", - "email": "soren@abicart.se" - } - ], - "description": "WebSocket client and server", - "support": { - "issues": "https://github.com/Textalk/websocket-php/issues", - "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" - }, - "time": "2022-03-29T09:46:59+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "twig/twig", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.7.1" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2023-08-28T11:09:02+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=8.0.0", - "ext-curl": "*", - "ext-imagick": "*", - "ext-mbstring": "*", - "ext-json": "*", - "ext-yaml": "*", - "ext-dom": "*", - "ext-redis": "*", - "ext-swoole": "*", - "ext-pdo": "*", - "ext-openssl": "*", - "ext-zlib": "*", - "ext-sockets": "*" - }, - "platform-dev": { - "ext-fileinfo": "*" - }, - "platform-overrides": { - "php": "8.0" - }, - "plugin-api-version": "2.2.0" -} From c3af1e45139a456195772dd606e81ab62442c056 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 23:49:50 +0400 Subject: [PATCH 120/144] chore: revert composer --- composer.lock | 6023 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6023 insertions(+) create mode 100644 composer.lock diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..7ccc6df4df --- /dev/null +++ b/composer.lock @@ -0,0 +1,6023 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e34e098996ca4769e2a956b423522e91", + "packages": [ + { + "name": "adhocore/jwt", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-jwt.git", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", + "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Jwt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Ultra lightweight JSON web token (JWT) library for PHP5.5+.", + "keywords": [ + "auth", + "json-web-token", + "jwt", + "jwt-auth", + "jwt-php", + "token" + ], + "support": { + "issues": "https://github.com/adhocore/php-jwt/issues", + "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + } + ], + "time": "2021-02-20T09:56:44+00:00" + }, + { + "name": "appwrite/appwrite", + "version": "10.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-for-php.git", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390", + "reference": "461eedf4efd502dc905c3055f36f0e3583f67390", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\": "src/Appwrite" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", + "support": { + "email": "team@appwrite.io", + "issues": "https://github.com/appwrite/sdk-for-php/issues", + "source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0", + "url": "https://appwrite.io/support" + }, + "time": "2023-09-07T23:28:31+00:00" + }, + { + "name": "appwrite/php-clamav", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/appwrite/php-clamav.git", + "reference": "f3897169f5c1f365312238a516ae9465f804634f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/php-clamav/zipball/f3897169f5c1f365312238a516ae9465f804634f", + "reference": "f3897169f5c1f365312238a516ae9465f804634f", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\ClamAV\\": "src/ClamAV" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "ClamAV network and pipe client for PHP", + "keywords": [ + "anti virus", + "appwrite", + "clamav", + "php" + ], + "support": { + "issues": "https://github.com/appwrite/php-clamav/issues", + "source": "https://github.com/appwrite/php-clamav/tree/2.0.0" + }, + "time": "2023-02-24T09:50:42+00:00" + }, + { + "name": "appwrite/php-runtimes", + "version": "0.13.1", + "source": { + "type": "git", + "url": "https://github.com/appwrite/runtimes.git", + "reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610" + }, + "require": { + "php": ">=8.0", + "utopia-php/system": "0.7.*" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\Runtimes\\": "src/Runtimes" + } + }, + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "Appwrite repository for Cloud Function runtimes that contains the configurations and tests for all of the Appwrite runtime environments.", + "keywords": [ + "appwrite", + "php", + "runtimes" + ], + "time": "2023-10-16T15:39:53+00:00" + }, + { + "name": "chillerlan/php-qrcode", + "version": "4.3.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-qrcode.git", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d", + "shasum": "" + }, + "require": { + "chillerlan/php-settings-container": "^2.1.4", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5", + "setasign/fpdf": "^1.8.2" + }, + "suggest": { + "chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.", + "setasign/fpdf": "Required to use the QR FPDF output." + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\QRCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kazuhiko Arase", + "homepage": "https://github.com/kazuhikoarase" + }, + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + }, + { + "name": "Contributors", + "homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors" + } + ], + "description": "A QR code generator. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-qrcode", + "keywords": [ + "phpqrcode", + "qr", + "qr code", + "qrcode", + "qrcode-generator" + ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-25T09:12:45+00:00" + }, + { + "name": "chillerlan/php-settings-container", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/chillerlan/php-settings-container.git", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "reference": "1beb7df3c14346d4344b0b2e12f6f9a74feabd4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phan/phan": "^5.3", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "chillerlan\\Settings\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Smiley", + "email": "smiley@chillerlan.net", + "homepage": "https://github.com/codemasher" + } + ], + "description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+", + "homepage": "https://github.com/chillerlan/php-settings-container", + "keywords": [ + "PHP7", + "Settings", + "configuration", + "container", + "helper" + ], + "support": { + "issues": "https://github.com/chillerlan/php-settings-container/issues", + "source": "https://github.com/chillerlan/php-settings-container" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4", + "type": "custom" + }, + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], + "time": "2022-07-05T22:32:14+00:00" + }, + { + "name": "colinmollenhour/credis", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "suggest": { + "ext-redis": "Improved performance for communicating with redis" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "support": { + "issues": "https://github.com/colinmollenhour/credis/issues", + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" + }, + "time": "2023-04-18T15:34:23+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", + "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2022-09-10T18:51:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:20:53+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-08-03T15:11:55+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:13:57+00:00" + }, + { + "name": "influxdb/influxdb-php", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/influxdata/influxdb-php.git", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": "^5.5 || ^7.0 || ^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "Curl extension, needed for Curl driver", + "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB\\": "src/InfluxDB" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Hoogendijk", + "email": "stephen@tca0.nl" + }, + { + "name": "Daniel Martinez", + "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" + } + ], + "description": "InfluxDB client library for PHP", + "keywords": [ + "client", + "influxdata", + "influxdb", + "influxdb class", + "influxdb client", + "influxdb library", + "time series" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-php/issues", + "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" + }, + "abandoned": true, + "time": "2020-12-26T17:45:17+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^0.12.66", + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + }, + "time": "2021-10-08T21:21:46+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", + "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11.0", + "illuminate/view": "^9.32.0", + "laravel-zero/framework": "^9.2.0", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.2.0", + "nunomaduro/termwind": "^1.14.0", + "pestphp/pest": "^1.22.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2022-11-29T16:25:20+00:00" + }, + { + "name": "league/csv", + "version": "9.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1", + "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.0", + "phpstan/phpstan-phpunit": "^0.12.0", + "phpstan/phpstan-strict-rules": "^0.12.0", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2021-04-17T16:32:08+00:00" + }, + { + "name": "matomo/device-detector", + "version": "6.1.6", + "source": { + "type": "git", + "url": "https://github.com/matomo-org/device-detector.git", + "reference": "5cbea85106e561c7138d03603eb6e05128480409" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409", + "reference": "5cbea85106e561c7138d03603eb6e05128480409", + "shasum": "" + }, + "require": { + "mustangostang/spyc": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "piwik/device-detector": "self.version" + }, + "require-dev": { + "matthiasmullie/scrapbook": "^1.4.7", + "mayflower/mo4-coding-standard": "^v8.0.0", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8.5.8", + "psr/cache": "^1.0.1", + "psr/simple-cache": "^1.0.1", + "symfony/yaml": "^5.1.7" + }, + "suggest": { + "doctrine/cache": "Can directly be used for caching purpose", + "ext-yaml": "Necessary for using the Pecl YAML parser" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeviceDetector\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The Matomo Team", + "email": "hello@matomo.org", + "homepage": "https://matomo.org/team/" + } + ], + "description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.", + "homepage": "https://matomo.org", + "keywords": [ + "devicedetection", + "parser", + "useragent" + ], + "support": { + "forum": "https://forum.matomo.org/", + "issues": "https://github.com/matomo-org/device-detector/issues", + "source": "https://github.com/matomo-org/matomo", + "wiki": "https://dev.matomo.org/" + }, + "time": "2023-10-02T10:01:54+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.11.0", + "jean85/pretty-package-versions": "^1.2 || ^2.0.1", + "php": "^7.1 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "squizlabs/php_codesniffer": "^3.6", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + }, + "time": "2021-10-20T22:22:37+00:00" + }, + { + "name": "mustangostang/spyc", + "version": "0.6.3", + "source": { + "type": "git", + "url": "git@github.com:mustangostang/spyc.git", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "time": "2019-09-10T13:16:29+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2023-03-06T14:43:22+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "resque/php-resque", + "version": "v1.3.6", + "source": { + "type": "git", + "url": "https://github.com/resque/php-resque.git", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", + "reference": "fe41c04763699b1318d97ed14cc78583e9380161", + "shasum": "" + }, + "require": { + "colinmollenhour/credis": "~1.7", + "php": ">=5.6.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", + "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", + "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." + }, + "bin": [ + "bin/resque", + "bin/resque-scheduler" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Resque": "lib", + "ResqueScheduler": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Hunsaker", + "email": "danhunsaker+resque@gmail.com", + "role": "Maintainer" + }, + { + "name": "Rajib Ahmed", + "homepage": "https://github.com/rajibahmed", + "role": "Maintainer" + }, + { + "name": "Steve Klabnik", + "email": "steve@steveklabnik.com", + "role": "Maintainer" + }, + { + "name": "Chris Boulton", + "email": "chris@bigcommerce.com", + "role": "Creator" + } + ], + "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", + "homepage": "http://www.github.com/resque/php-resque/", + "keywords": [ + "background", + "job", + "redis", + "resque" + ], + "support": { + "issues": "https://github.com/resque/php-resque/issues", + "source": "https://github.com/resque/php-resque/tree/v1.3.6" + }, + "time": "2020-04-16T16:39:50+00:00" + }, + { + "name": "slickdeals/statsd", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/225588a0a079e145359049f6e5e23eedb1b4c17f", + "reference": "225588a0a079e145359049f6e5e23eedb1b4c17f", + "shasum": "" + }, + "require": { + "php": ">= 7.3 || ^8" + }, + "replace": { + "domnikl/statsd": "self.version" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://github.com/Slickdeals/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "issues": "https://github.com/Slickdeals/statsd-php/issues", + "source": "https://github.com/Slickdeals/statsd-php/tree/3.1.0" + }, + "time": "2021-06-04T20:33:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "utopia-php/abuse", + "version": "0.31.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/abuse.git", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Abuse\\": "src/Abuse" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple abuse library to manage application usage limits", + "keywords": [ + "Abuse", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/abuse/issues", + "source": "https://github.com/utopia-php/abuse/tree/0.31.1" + }, + "time": "2023-08-29T11:07:46+00:00" + }, + { + "name": "utopia-php/analytics", + "version": "0.10.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/analytics.git", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "^0.15.0" + }, + "require-dev": { + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Analytics\\": "src/Analytics" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to track events & users.", + "keywords": [ + "analytics", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/analytics/issues", + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + }, + "time": "2023-03-22T12:01:09+00:00" + }, + { + "name": "utopia-php/audit", + "version": "0.33.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/audit.git", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/database": "0.43.*" + }, + "require-dev": { + "laravel/pint": "1.5.*", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Audit\\": "src/Audit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple audit library to manage application users logs", + "keywords": [ + "Audit", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/audit/issues", + "source": "https://github.com/utopia-php/audit/tree/0.33.1" + }, + "time": "2023-08-29T11:07:40+00:00" + }, + { + "name": "utopia-php/cache", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cache.git", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/212e66100a1f32e674fca5d9bc317cc998303089", + "reference": "212e66100a1f32e674fca5d9bc317cc998303089", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-memcached": "*", + "ext-redis": "*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Cache\\": "src/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple cache library to manage application cache storing, loading and purging", + "keywords": [ + "cache", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/0.8.0" + }, + "time": "2022-10-16T16:48:09+00:00" + }, + { + "name": "utopia-php/cli", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/cli.git", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\CLI\\": "src/CLI" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple CLI library to manage command line applications", + "keywords": [ + "cli", + "command line", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/cli/issues", + "source": "https://github.com/utopia-php/cli/tree/0.15.0" + }, + "time": "2023-03-01T05:55:14+00:00" + }, + { + "name": "utopia-php/config", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/config.git", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/config/zipball/a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "reference": "a3d7bc0312d7150d5e04b1362dc34b2b136908cc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Config\\": "src/Config" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Config library to managing application config variables", + "keywords": [ + "config", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/config/issues", + "source": "https://github.com/utopia-php/config/tree/0.2.2" + }, + "time": "2020-10-24T09:49:09+00:00" + }, + { + "name": "utopia-php/database", + "version": "0.43.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/database.git", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5f7b05189cfbcc0506090498c580c5765375a00a", + "reference": "5f7b05189cfbcc0506090498c580c5765375a00a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-pdo": "*", + "php": ">=8.0", + "utopia-php/cache": "0.8.*", + "utopia-php/framework": "0.*.*", + "utopia-php/mongo": "0.3.*" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.4.*", + "pcov/clobber": "^2.0", + "phpstan/phpstan": "1.10.*", + "phpunit/phpunit": "^9.4", + "rregeer/phpunit-coverage-check": "^0.3.1", + "swoole/ide-helper": "4.8.0", + "utopia-php/cli": "^0.14.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Database\\": "src/Database" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to manage application persistence using multiple database adapters", + "keywords": [ + "database", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.43.5" + }, + "time": "2023-10-06T06:49:47+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.3.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Domains\\": "src/Domains" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" + } + ], + "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "domains", + "framework", + "icann", + "php", + "public suffix", + "tld", + "tld extract", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/domains/issues", + "source": "https://github.com/utopia-php/domains/tree/0.3.2" + }, + "time": "2023-07-19T16:39:24+00:00" + }, + { + "name": "utopia-php/dsn", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dsn.git", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DSN\\": "src/DSN" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library for parsing and managing Data Source Names ( DSNs )", + "keywords": [ + "dsn", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dsn/issues", + "source": "https://github.com/utopia-php/dsn/tree/0.1.0" + }, + "time": "2022-10-26T10:06:20+00:00" + }, + { + "name": "utopia-php/framework", + "version": "0.31.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/framework.git", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0", + "reference": "207f77378965fca9a9bc3783ea379d3549f86bc0", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP framework", + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/framework/issues", + "source": "https://github.com/utopia-php/framework/tree/0.31.0" + }, + "time": "2023-08-30T16:10:04+00:00" + }, + { + "name": "utopia-php/image", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/image.git", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336", + "reference": "ca5f436f9aa22dedaa6648f24f3687733808e336", + "shasum": "" + }, + "require": { + "ext-imagick": "*", + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Image\\": "src/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple Image manipulation library", + "keywords": [ + "framework", + "image", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/image/issues", + "source": "https://github.com/utopia-php/image/tree/0.5.4" + }, + "time": "2022-05-11T12:30:41+00:00" + }, + { + "name": "utopia-php/locale", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/locale.git", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/locale/zipball/c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "reference": "c2d9358d0fe2f6b6ed5448369f9d1e430c615447", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Locale\\": "src/Locale" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple locale library to manage application translations", + "keywords": [ + "framework", + "locale", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/locale/issues", + "source": "https://github.com/utopia-php/locale/tree/0.4.0" + }, + "time": "2021-07-24T11:35:55+00:00" + }, + { + "name": "utopia-php/logger", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/logger.git", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.x-dev", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Logger\\": "src/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", + "keywords": [ + "appsignal", + "errors", + "framework", + "logger", + "logging", + "logs", + "php", + "raygun", + "sentry", + "upf", + "utopia", + "warnings" + ], + "support": { + "issues": "https://github.com/utopia-php/logger/issues", + "source": "https://github.com/utopia-php/logger/tree/0.3.1" + }, + "time": "2023-02-10T15:52:50+00:00" + }, + { + "name": "utopia-php/messaging", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/messaging.git", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Messaging\\": "src/Utopia/Messaging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP messaging library", + "keywords": [ + "library", + "messaging", + "php", + "upf", + "utopia", + "utopia-php" + ], + "support": { + "issues": "https://github.com/utopia-php/messaging/issues", + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + }, + "time": "2023-09-14T20:48:42+00:00" + }, + { + "name": "utopia-php/migration", + "version": "0.3.5", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/migration.git", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "shasum": "" + }, + "require": { + "appwrite/appwrite": "10.0.*", + "php": "8.*", + "utopia-php/cli": "0.*" + }, + "require-dev": { + "laravel/pint": "1.*", + "phpunit/phpunit": "9.*", + "vlucas/phpdotenv": "5.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Migration\\": "src/Migration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to migrate resources between services.", + "keywords": [ + "framework", + "migration", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/0.3.5" + }, + "time": "2023-09-25T16:51:47+00:00" + }, + { + "name": "utopia-php/mongo", + "version": "0.3.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "reference": "52326a9a43e2d27ff0c15c48ba746dacbe9a7aee", + "shasum": "" + }, + "require": { + "ext-mongodb": "*", + "mongodb/mongodb": "1.10.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.14", + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "4.8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.3.1" + }, + "time": "2023-09-01T17:25:28+00:00" + }, + { + "name": "utopia-php/orchestration", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/orchestration.git", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Orchestration\\": "src/Orchestration" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lite & fast micro PHP abstraction library for container orchestration", + "keywords": [ + "docker", + "framework", + "kubernetes", + "orchestration", + "php", + "swarm", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/orchestration/issues", + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + }, + "time": "2023-03-17T15:05:06+00:00" + }, + { + "name": "utopia-php/platform", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/platform.git", + "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/229a7b1fa1f39afd1532f7a515326a6afc222a26", + "reference": "229a7b1fa1f39afd1532f7a515326a6afc222a26", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-redis": "*", + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Platform\\": "src/Platform" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Light and Fast Platform Library", + "keywords": [ + "framework", + "php", + "platform", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/platform/issues", + "source": "https://github.com/utopia-php/platform/tree/0.5.0" + }, + "time": "2023-10-16T20:28:49+00:00" + }, + { + "name": "utopia-php/pools", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", + "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.4.2" + }, + "time": "2022-11-22T07:55:45+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, + { + "name": "utopia-php/queue", + "version": "0.5.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/queue.git", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.8.8", + "workerman/workerman": "^4.0" + }, + "suggest": { + "ext-swoole": "Needed to support Swoole.", + "workerman/workerman": "Needed to support Workerman." + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Queue\\": "src/Queue" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A powerful task queue.", + "keywords": [ + "Tasks", + "framework", + "php", + "queue", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/queue/issues", + "source": "https://github.com/utopia-php/queue/tree/0.5.3" + }, + "time": "2023-05-24T19:06:04+00:00" + }, + { + "name": "utopia-php/registry", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/registry.git", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/registry/zipball/bedc4ed54527b2803e6dfdccc39449f98522b70d", + "reference": "bedc4ed54527b2803e6dfdccc39449f98522b70d", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Registry\\": "src/Registry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "A simple dependency management library for PHP", + "keywords": [ + "dependency management", + "di", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/registry/issues", + "source": "https://github.com/utopia-php/registry/tree/0.5.0" + }, + "time": "2021-03-10T10:45:22+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.17.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "shasum": "" + }, + "require": { + "ext-brotli": "*", + "ext-fileinfo": "*", + "ext-lz4": "*", + "ext-snappy": "*", + "ext-xz": "*", + "ext-zlib": "*", + "ext-zstd": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Storage\\": "src/Storage" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Storage library to manage application storage", + "keywords": [ + "framework", + "php", + "storage", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/storage/issues", + "source": "https://github.com/utopia-php/storage/tree/0.17.0" + }, + "time": "2023-08-21T11:28:36+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "reference": "c2a3a4f944a2f22945af3cbcb95b13f0769628b1", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.*.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "4.8.3", + "vimeo/psalm": "4.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.5.0" + }, + "time": "2022-10-19T22:19:07+00:00" + }, + { + "name": "utopia-php/system", + "version": "0.7.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/system.git", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/system/zipball/01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "reference": "01bf0d283aded0ee0a7a6e5ff540acf64270ab27", + "shasum": "" + }, + "require": { + "laravel/pint": "1.2.*", + "php": ">=8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\System\\": "src/System" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple library for obtaining information about the host's system.", + "keywords": [ + "framework", + "php", + "system", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/system/issues", + "source": "https://github.com/utopia-php/system/tree/0.7.1" + }, + "time": "2023-08-30T09:14:37+00:00" + }, + { + "name": "utopia-php/vcs", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/vcs.git", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "shasum": "" + }, + "require": { + "adhocore/jwt": "^1.1", + "php": ">=8.0", + "utopia-php/cache": "^0.8.0", + "utopia-php/framework": "0.31.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\VCS\\": "src/VCS", + "Utopia\\Detector\\": "src/Detector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library to integrate version control systems like GitHub, GitLab etc. to receive webhook events", + "keywords": [ + "framework", + "php", + "utopia", + "vcs" + ], + "support": { + "issues": "https://github.com/utopia-php/vcs/issues", + "source": "https://github.com/utopia-php/vcs/tree/0.5.0" + }, + "time": "2023-09-13T19:05:52+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.5", + "swoole/ide-helper": "4.6.6", + "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", + "workerman/workerman": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\WebSocket\\": "src/WebSocket" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], + "description": "A simple abstraction for WebSocket servers.", + "keywords": [ + "framework", + "php", + "upf", + "utopia", + "websocket" + ], + "support": { + "issues": "https://github.com/utopia-php/websocket/issues", + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + }, + "time": "2021-12-20T10:50:09+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webonyx/graphql-php", + "version": "v14.11.10", + "source": { + "type": "git", + "url": "https://github.com/webonyx/graphql-php.git", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" + }, + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" + }, + "type": "library", + "autoload": { + "psr-4": { + "GraphQL\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", + "keywords": [ + "api", + "graphql" + ], + "support": { + "issues": "https://github.com/webonyx/graphql-php/issues", + "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + }, + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2023-07-05T14:23:37+00:00" + } + ], + "packages-dev": [ + { + "name": "appwrite/sdk-generator", + "version": "0.35.2", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-generator.git", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b", + "reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "matthiasmullie/minify": "^1.3.68", + "php": ">=8.0", + "twig/twig": "^3.4.1" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "phpunit/phpunit": "^9.5.21", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\SDK\\": "src/SDK", + "Appwrite\\Spec\\": "src/Spec" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "support": { + "issues": "https://github.com/appwrite/sdk-generator/issues", + "source": "https://github.com/appwrite/sdk-generator/tree/0.35.2" + }, + "time": "2023-09-14T14:59:50+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.71", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.1", + "php": ">=5.3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": ">=2.0", + "matthiasmullie/scrapbook": ">=1.3", + "phpunit/phpunit": ">=4.8", + "squizlabs/php_codesniffer": ">=3.0" + }, + "suggest": { + "psr/cache-implementation": "Cache implementation to use with Minify::cache" + }, + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "https://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.", + "homepage": "https://github.com/matthiasmullie/minify", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ], + "support": { + "issues": "https://github.com/matthiasmullie/minify/issues", + "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + }, + "funding": [ + { + "url": "https://github.com/matthiasmullie", + "type": "github" + } + ], + "time": "2023-04-25T20:33:03+00:00" + }, + { + "name": "matthiasmullie/path-converter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/path-converter.git", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "MatthiasMullie\\PathConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "pathconverter@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "Relative path converter", + "homepage": "http://github.com/matthiasmullie/path-converter", + "keywords": [ + "converter", + "path", + "paths", + "relative" + ], + "support": { + "issues": "https://github.com/matthiasmullie/path-converter/issues", + "source": "https://github.com/matthiasmullie/path-converter/tree/1.1.3" + }, + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" + }, + "time": "2023-02-02T15:41:36+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "bcad8d995980440892759db0c32acae7c8e79442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + }, + "time": "2023-09-26T12:28:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-19T04:57:46+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.0", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-01T12:37:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/5.0.2" + }, + "time": "2023-03-20T06:05:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "reference": "1712325e99b6bf869ccbf9bf41ab749e7328ea46", + "shasum": "" + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.5.7" + }, + "time": "2022-03-29T09:46:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-08-28T11:09:02+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0.0", + "ext-curl": "*", + "ext-imagick": "*", + "ext-mbstring": "*", + "ext-json": "*", + "ext-yaml": "*", + "ext-dom": "*", + "ext-redis": "*", + "ext-swoole": "*", + "ext-pdo": "*", + "ext-openssl": "*", + "ext-zlib": "*", + "ext-sockets": "*" + }, + "platform-dev": { + "ext-fileinfo": "*" + }, + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.2.0" +} From b8367c2b9ff29e24a67dd49583b87d54d6d19453 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 17 Oct 2023 23:51:16 +0400 Subject: [PATCH 121/144] chore: update composer --- composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index 7ccc6df4df..e45d242295 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": "e34e098996ca4769e2a956b423522e91", + "content-hash": "5060c891e467051fedd25f3853cd37b0", "packages": [ { "name": "adhocore/jwt", @@ -2516,16 +2516,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.2.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", "shasum": "" }, "require": { @@ -2534,8 +2534,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" }, "type": "library", "autoload": { @@ -2558,9 +2558,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" }, - "time": "2023-09-14T20:48:42+00:00" + "time": "2023-02-07T05:42:46+00:00" }, { "name": "utopia-php/migration", @@ -6019,5 +6019,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } From 6a53d8e0d5d086cd5729ad235a73b68f9f147c1e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 00:02:45 +0400 Subject: [PATCH 122/144] chore: update composer --- composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index e45d242295..7ccc6df4df 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": "5060c891e467051fedd25f3853cd37b0", + "content-hash": "e34e098996ca4769e2a956b423522e91", "packages": [ { "name": "adhocore/jwt", @@ -2516,16 +2516,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.1.1", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", "shasum": "" }, "require": { @@ -2534,8 +2534,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" }, "type": "library", "autoload": { @@ -2558,9 +2558,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" }, - "time": "2023-02-07T05:42:46+00:00" + "time": "2023-09-14T20:48:42+00:00" }, { "name": "utopia-php/migration", @@ -6019,5 +6019,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.2.0" } From eeb6a5065bc607a3fa932fac7ee9e478c05641d7 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 00:11:52 +0400 Subject: [PATCH 123/144] chore: update composer --- composer.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 7ccc6df4df..60a8bf6393 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": "e34e098996ca4769e2a956b423522e91", + "content-hash": "5060c891e467051fedd25f3853cd37b0", "packages": [ { "name": "adhocore/jwt", @@ -2516,16 +2516,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.2.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", "shasum": "" }, "require": { @@ -2534,8 +2534,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" }, "type": "library", "autoload": { @@ -2558,9 +2558,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" }, - "time": "2023-09-14T20:48:42+00:00" + "time": "2023-02-07T05:42:46+00:00" }, { "name": "utopia-php/migration", From 011df60c0d22d4c8e00480db1045d7473cac1530 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 01:17:02 +0400 Subject: [PATCH 124/144] feat: remove old worker class --- src/Appwrite/Resque/Worker.php | 409 ------------------ .../Account/AccountCustomClientTest.php | 2 +- 2 files changed, 1 insertion(+), 410 deletions(-) delete mode 100644 src/Appwrite/Resque/Worker.php diff --git a/src/Appwrite/Resque/Worker.php b/src/Appwrite/Resque/Worker.php deleted file mode 100644 index 7693f1e6ff..0000000000 --- a/src/Appwrite/Resque/Worker.php +++ /dev/null @@ -1,409 +0,0 @@ -init(); - } catch (\Throwable $error) { - foreach (self::$errorCallbacks as $errorCallback) { - $errorCallback($error, "init", $this->getName()); - } - - throw $error; - } - } - - /** - * A wrapper around 'run' function with non-worker-specific code - * - * @return void - * @throws \Exception|\Throwable - */ - public function perform(): void - { - try { - /** - * Disabling global authorization in workers. - */ - Authorization::disable(); - Authorization::setDefaultStatus(false); - $this->run(); - } catch (\Throwable $error) { - foreach (self::$errorCallbacks as $errorCallback) { - $errorCallback($error, "run", $this->getName(), $this->args); - } - - throw $error; - } - } - - /** - * A wrapper around 'shutdown' function with non-worker-specific code - * - * @return void - * @throws \Exception|\Throwable - */ - public function tearDown(): void - { - global $register; - - try { - $pools = $register->get('pools'); /** @var Group $pools */ - $pools->reclaim(); - - $this->shutdown(); - } catch (\Throwable $error) { - foreach (self::$errorCallbacks as $errorCallback) { - $errorCallback($error, "shutdown", $this->getName()); - } - - throw $error; - } - } - - - /** - * Register callback. Will be executed when error occurs. - * @param callable $callback - * @return void - */ - public static function error(callable $callback): void - { - self::$errorCallbacks[] = $callback; - } - - /** - * Get internal project database - * @param Document $project - * @return Database - * @throws Exception - */ - protected static $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - protected function getProjectDB(Document $project): Database - { - global $register; - - $pools = $register->get('pools'); /** @var Group $pools */ - - if ($project->isEmpty() || $project->getId() === 'console') { - return $this->getConsoleDB(); - } - - $databaseName = $project->getAttribute('database'); - - if (isset(self::$databases[$databaseName])) { - $database = self::$databases[$databaseName]; - $database->setNamespace('_' . $project->getInternalId()); - return $database; - } - - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource() - ; - - $database = new Database($dbAdapter, $this->getCache()); - - self::$databases[$databaseName] = $database; - - $database->setNamespace('_' . $project->getInternalId()); - - return $database; - } - - /** - * Get console database - * @return Database - * @throws Exception - */ - protected function getConsoleDB(): Database - { - global $register; - - $pools = $register->get('pools'); /** @var Group $pools */ - - $databaseName = 'console'; - - if (isset(self::$databases[$databaseName])) { - $database = self::$databases[$databaseName]; - $database->setNamespace('_console'); - return $database; - } - - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource() - ; - - $database = new Database($dbAdapter, $this->getCache()); - - self::$databases[$databaseName] = $database; - - $database->setNamespace('_console'); - - return $database; - } - - - /** - * Get Cache - * @return Cache - */ - protected function getCache(): Cache - { - global $register; - - $pools = $register->get('pools'); /** @var Group $pools */ - - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); - } - - /** - * Get usage queue - * @return Usage - * @throws Exception - */ - protected function getUsageQueue(): Usage - { - global $register; - - $pools = $register->get('pools'); /** @var Group $pools */ - $queue = $pools - ->get('queue') - ->pop() - ->getResource(); - - return new Usage($queue); - } - - /** - * Get Functions Storage Device - * @param string $projectId of the project - * @return Device - */ - protected function getFunctionsDevice(string $projectId): Device - { - return $this->getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); - } - - /** - * Get Files Storage Device - * @param string $projectId of the project - * @return Device - */ - protected function getFilesDevice(string $projectId): Device - { - return $this->getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); - } - - /** - * Get Builds Storage Device - * @param string $projectId of the project - * @return Device - */ - protected function getBuildsDevice(string $projectId): Device - { - return $this->getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); - } - - protected function getCacheDevice(string $projectId): Device - { - return $this->getDevice(APP_STORAGE_CACHE . '/app-' . $projectId); - } - - /** - * Get Device based on selected storage environment - * @param string $root path of the device - * @return Device - */ - public function getDevice(string $root): Device - { - $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Exception $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = App::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = App::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = App::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = App::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = App::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = App::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = App::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = App::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = App::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = App::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = App::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); - $backblazeAcl = 'private'; - return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); - case Storage::DEVICE_LINODE: - $linodeAccessKey = App::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = App::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = App::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = App::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); - $linodeAcl = 'private'; - return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); - case Storage::DEVICE_WASABI: - $wasabiAccessKey = App::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = App::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = App::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = App::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); - $wasabiAcl = 'private'; - return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); - } - } - } -} diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 1441ab7f98..0e0634850a 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -1011,7 +1011,7 @@ class AccountCustomClientTest extends Scope $smsRequest = $this->getLastRequest(); return \array_merge($data, [ - 'token' => $smsRequest['data']['message'] + 'token' => $smsRequest['data']['secret'] ]); } From 67642f9f918c19d405461e073e05a7697fd99fc9 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 01:56:03 +0400 Subject: [PATCH 125/144] chore: update docker compose config --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4e34b1bad9..78feecd17e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -211,7 +211,6 @@ services: - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime - traefik.http.routers.appwrite_realtime_wss.tls=true - - traefik.http.routers.appwrite_realtime_wss.tls.certresolver=dns networks: - appwrite volumes: From 39820b5a0f97c9ae4ce5b005a037ddbcf7aae02a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 01:56:54 +0400 Subject: [PATCH 126/144] chore: update docker compose config --- app/views/install/compose.phtml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 8928107029..2c836c575c 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -188,7 +188,6 @@ services: - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime - traefik.http.routers.appwrite_realtime_wss.tls=true - - traefik.http.routers.appwrite_realtime_wss.tls.certresolver=dns networks: - appwrite depends_on: From 33ec5b3d7b1b7fdca671a000f099817dcd36a4ce Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 02:36:50 +0400 Subject: [PATCH 127/144] chore: re-order tests --- app/worker.php | 1 + phpunit.xml | 8 ++++---- src/Appwrite/Platform/Workers/Deletes.php | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/worker.php b/app/worker.php index 8aa52ab931..bc15e44c25 100644 --- a/app/worker.php +++ b/app/worker.php @@ -271,6 +271,7 @@ $worker Console::error('[Error] Message: ' . $error->getMessage()); Console::error('[Error] File: ' . $error->getFile()); Console::error('[Error] Line: ' . $error->getLine()); + Console::error('[Error] Trace: ' . $error->getTraceAsString()); }); $worker->workerStart() diff --git a/phpunit.xml b/phpunit.xml index f83f9f0fae..975b54962a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -19,9 +19,11 @@ ./tests/e2e/Client.php ./tests/e2e/General ./tests/e2e/Scopes - ./tests/e2e/Services/Account - ./tests/e2e/Services/Console + ./tests/e2e/Services/Teams ./tests/e2e/Services/Realtime + ./tests/e2e/Services/Account + ./tests/e2e/Services/Users + ./tests/e2e/Services/Console ./tests/e2e/Services/Avatars ./tests/e2e/Services/Databases ./tests/e2e/Services/GraphQL @@ -29,8 +31,6 @@ ./tests/e2e/Services/Locale ./tests/e2e/Services/Projects ./tests/e2e/Services/Storage - ./tests/e2e/Services/Teams - ./tests/e2e/Services/Users ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Functions/FunctionsBase.php ./tests/e2e/Services/Functions/FunctionsCustomServerTest.php diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 7e43bed6bd..e5800462db 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -806,7 +806,7 @@ class Deletes extends Action * Delete builds */ Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $$getBuildsDevice($projectId); + $storageBuilds = $getBuildsDevice($projectId); $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) ], $dbForProject, function (Document $document) use ($storageBuilds) { From c501dc61dc688dd8a48e50826f278682c85b093d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 02:58:29 +0400 Subject: [PATCH 128/144] chore: re-order tests --- phpunit.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 975b54962a..d39dde6514 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" + stopOnFailure="true" > @@ -21,6 +21,7 @@ ./tests/e2e/Scopes ./tests/e2e/Services/Teams ./tests/e2e/Services/Realtime + ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Account ./tests/e2e/Services/Users ./tests/e2e/Services/Console @@ -31,7 +32,6 @@ ./tests/e2e/Services/Locale ./tests/e2e/Services/Projects ./tests/e2e/Services/Storage - ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Functions/FunctionsBase.php ./tests/e2e/Services/Functions/FunctionsCustomServerTest.php ./tests/e2e/Services/Functions/FunctionsCustomClientTest.php From a0be1158156a8840d1775a8398c1f71ef3ef0414 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 03:07:41 +0400 Subject: [PATCH 129/144] chore: re-order tests --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index d39dde6514..3edf690127 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" > From 2a201a9d1cb3e4391ccf6d9028949fc910a604f0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:42:23 +0400 Subject: [PATCH 130/144] chore: fix flaky tests --- .../Realtime/RealtimeConsoleClientTest.php | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index 9c91ac4a52..81498f6160 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -346,13 +346,13 @@ class RealtimeConsoleClientTest extends Scope /** * Test Delete Index */ - $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actorsId . '/indexes/key_name', array_merge([ + $indexKey = 'key_name'; + $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actorsId . '/indexes/' . $indexKey , array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals($attribute['headers']['status-code'], 204); - $indexKey = 'key_name'; $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); @@ -370,6 +370,23 @@ class RealtimeConsoleClientTest extends Scope $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); + /** Delete index generates two events 1 from the API and one from the Worker */ + $response = json_decode($client->receive(), true); + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(1, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + $client->close(); return $data; @@ -402,13 +419,13 @@ class RealtimeConsoleClientTest extends Scope /** * Test Delete Attribute */ - $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['actorsId'] . '/attributes/name', array_merge([ + $attributeKey = 'name'; + $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['actorsId'] . '/attributes/' . $attributeKey, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals($attribute['headers']['status-code'], 204); - $attributeKey = 'name'; $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); From 5f9059e221abc9d1082e0da824f549351696363f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:50:40 +0400 Subject: [PATCH 131/144] chore: run linter --- tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index 81498f6160..99ea98a499 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -347,7 +347,7 @@ class RealtimeConsoleClientTest extends Scope * Test Delete Index */ $indexKey = 'key_name'; - $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actorsId . '/indexes/' . $indexKey , array_merge([ + $attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actorsId . '/indexes/' . $indexKey, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -370,7 +370,7 @@ class RealtimeConsoleClientTest extends Scope $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); - /** Delete index generates two events 1 from the API and one from the Worker */ + /** Delete index generates two events. One from the API and one from the database worker */ $response = json_decode($client->receive(), true); $this->assertArrayHasKey('type', $response); $this->assertArrayHasKey('data', $response); From 050cb28d4f227ae9e8291191c38d292eb44ad46f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 20:30:47 +0400 Subject: [PATCH 132/144] fix: incorrect events --- app/controllers/api/databases.php | 4 ++-- src/Appwrite/Platform/Workers/Databases.php | 1 + .../Realtime/RealtimeConsoleClientTest.php | 21 +++++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 20eeb19f82..ed21760c67 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2264,7 +2264,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->desc('Delete attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') - ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete') + ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') ->label('audits.event', 'attribute.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('usage.metric', 'collections.{scope}.requests.update') @@ -2642,7 +2642,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->desc('Delete index') ->groups(['api', 'database']) ->label('scope', 'collections.write') - ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete') + ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update') ->label('audits.event', 'index.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('usage.metric', 'collections.{scope}.requests.update') diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 8f62df3982..8dca081495 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -466,6 +466,7 @@ class Databases extends Action throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); + $index->setAttribute('status', 'deleted'); } catch (\Exception $e) { Console::error($e->getMessage()); diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index 99ea98a499..82068f1301 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -362,10 +362,10 @@ class RealtimeConsoleClientTest extends Scope $this->assertArrayHasKey('timestamp', $response['data']); $this->assertCount(1, $response['data']['channels']); $this->assertContains('console', $response['data']['channels']); - $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); - $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.delete", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.indexes.*.update", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.*.indexes.*", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); $this->assertNotEmpty($response['data']['payload']); @@ -428,6 +428,23 @@ class RealtimeConsoleClientTest extends Scope $this->assertEquals($attribute['headers']['status-code'], 204); $response = json_decode($client->receive(), true); + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertCount(1, $response['data']['channels']); + $this->assertContains('console', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*.attributes.*", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + + $response = json_decode($client->receive(), true); + $this->assertArrayHasKey('type', $response); $this->assertArrayHasKey('data', $response); $this->assertEquals('event', $response['type']); From 699ac7f7f518dcfe4c8e1f4189c9aa7873d52c8e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 20:53:11 +0400 Subject: [PATCH 133/144] fix: review comments --- app/worker.php | 1 - phpunit.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/worker.php b/app/worker.php index bc15e44c25..8aa52ab931 100644 --- a/app/worker.php +++ b/app/worker.php @@ -271,7 +271,6 @@ $worker Console::error('[Error] Message: ' . $error->getMessage()); Console::error('[Error] File: ' . $error->getFile()); Console::error('[Error] Line: ' . $error->getLine()); - Console::error('[Error] Trace: ' . $error->getTraceAsString()); }); $worker->workerStart() diff --git a/phpunit.xml b/phpunit.xml index 3edf690127..975b54962a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -21,7 +21,6 @@ ./tests/e2e/Scopes ./tests/e2e/Services/Teams ./tests/e2e/Services/Realtime - ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Account ./tests/e2e/Services/Users ./tests/e2e/Services/Console @@ -32,6 +31,7 @@ ./tests/e2e/Services/Locale ./tests/e2e/Services/Projects ./tests/e2e/Services/Storage + ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Functions/FunctionsBase.php ./tests/e2e/Services/Functions/FunctionsCustomServerTest.php ./tests/e2e/Services/Functions/FunctionsCustomClientTest.php From 2e3d17e7b1d07e1c05533fa527c2388500b0d806 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 21:06:59 +0400 Subject: [PATCH 134/144] fix: webhook tests --- tests/e2e/Services/Webhooks/WebhooksBase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index 6e30e7abc1..e2eb29c74d 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -167,10 +167,10 @@ trait WebhooksBase $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.attributes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); From 20415c6daf9b916c773bcdd2501c329ce0c2524f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 21:09:23 +0400 Subject: [PATCH 135/144] fix: webhook tests --- tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 01aa00cd84..7f8031ce94 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -124,10 +124,10 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*', $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString('databases.' . $databaseId . '.collections.*.indexes.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $webhook['headers']['X-Appwrite-Webhook-Events']); - $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); + $this->assertStringContainsString("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); From 8a52dc3485863ae67bc862c11e86738ae804388e Mon Sep 17 00:00:00 2001 From: shimon Date: Wed, 18 Oct 2023 20:21:39 +0300 Subject: [PATCH 136/144] some fixes --- src/Appwrite/Platform/Workers/Deletes.php | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 7e43bed6bd..2130499257 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -205,7 +205,6 @@ class Deletes extends Action $dbForProject = $getProjectDB($project); $document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]); - if ($document) { $cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) @@ -387,7 +386,6 @@ class Deletes extends Action */ private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void { - $projects = $dbForConsole->find('projects', [ Query::equal('teamInternalId', [$document->getInternalId()]) ]); @@ -681,25 +679,25 @@ class Deletes extends Action * Delete Deployments */ Console::info("Deleting deployments for function " . $functionId); - $storageFunctions = $getFunctionsDevice($projectId); + $functionsStorage = $getFunctionsDevice($projectId); $deploymentInternalIds = []; $this->deleteByGroup('deployments', [ Query::equal('resourceInternalId', [$functionInternalId]) - ], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentInternalIds) { + ], $dbForProject, function (Document $document) use ($functionsStorage, &$deploymentInternalIds) { $deploymentInternalIds[] = $document->getInternalId(); - $this->deleteDeploymentFiles($storageFunctions, $document); + $this->deleteDeploymentFiles($functionsStorage, $document); }); /** * Delete builds */ Console::info("Deleting builds for function " . $functionId); - $storageBuilds = $getBuildsDevice($projectId); + $buildsStorage = $getBuildsDevice($projectId); foreach ($deploymentInternalIds as $deploymentInternalId) { $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) - ], $dbForProject, function (Document $document) use ($storageBuilds) { - $this->deleteBuildFiles($storageBuilds, $document); + ], $dbForProject, function (Document $document) use ($buildsStorage) { + $this->deleteBuildFiles($buildsStorage, $document); }); } @@ -799,18 +797,18 @@ class Deletes extends Action /** * Delete deployment files */ - $storageFunctions = $getFunctionsDevice($projectId); - $this->deleteDeploymentFiles($storageFunctions, $document); + $functionsStorage = $getFunctionsDevice($projectId); + $this->deleteDeploymentFiles($functionsStorage, $document); /** * Delete builds */ Console::info("Deleting builds for deployment " . $deploymentId); - $storageBuilds = $$getBuildsDevice($projectId); + $buildsStorage = $getBuildsDevice($projectId); $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) - ], $dbForProject, function (Document $document) use ($storageBuilds) { - $this->deleteBuildFiles($storageBuilds, $document); + ], $dbForProject, function (Document $document) use ($buildsStorage) { + $this->deleteBuildFiles($buildsStorage, $document); }); /** From 72d0865ad8108dfc474a137e32ec9343f5be40c1 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 18:54:20 +0000 Subject: [PATCH 137/144] chore: use deletes worker for deleting audit logs --- app/controllers/api/databases.php | 10 ++-- composer.lock | 60 +++++++++++---------- src/Appwrite/Platform/Workers/Databases.php | 13 ----- 3 files changed, 35 insertions(+), 48 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index cdfdd8172b..e98016c022 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3458,10 +3458,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') - ->inject('queueForDatabase') + ->inject('deletes') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $deletes, Event $queueForEvents, string $mode) { $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -3532,10 +3532,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $processDocument($collection, $document); - $queueForDatabase - ->setType(DATABASE_TYPE_DELETE_DOCUMENT) - ->setDatabase($database) - ->setCollection($collection) + $deletes + ->setType(DELETE_TYPE_AUDIT) ->setDocument($document); $queueForEvents diff --git a/composer.lock b/composer.lock index a381097e67..392a5b47e4 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": "509d6d460bad65092c5e3b8d6b390ec9", + "content-hash": "748f1a5e4fdf0a73ea26ddf0e7822f19", "packages": [ { "name": "adhocore/jwt", @@ -156,11 +156,11 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.13.0", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "5ab496b3908992b39275994a23783701c4b3de84" + "reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610" }, "require": { "php": ">=8.0", @@ -195,7 +195,7 @@ "php", "runtimes" ], - "time": "2023-09-12T19:38:43+00:00" + "time": "2023-10-16T15:39:53+00:00" }, { "name": "chillerlan/php-qrcode", @@ -1797,16 +1797,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.1.1", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", - "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", + "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", "shasum": "" }, "require": { @@ -1815,8 +1815,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*" + "phpmailer/phpmailer": "6.8.*", + "phpunit/phpunit": "9.6.*" }, "type": "library", "autoload": { @@ -1839,9 +1839,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.1" + "source": "https://github.com/utopia-php/messaging/tree/0.2.0" }, - "time": "2023-02-07T05:42:46+00:00" + "time": "2023-09-14T20:48:42+00:00" }, { "name": "utopia-php/migration", @@ -2007,12 +2007,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "056a60bb65eb12005f451714da0fe35a22f424c2" + "reference": "6c0a1b13864545145e62b051a914b7c3339716b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/056a60bb65eb12005f451714da0fe35a22f424c2", - "reference": "056a60bb65eb12005f451714da0fe35a22f424c2", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/6c0a1b13864545145e62b051a914b7c3339716b4", + "reference": "6c0a1b13864545145e62b051a914b7c3339716b4", "shasum": "" }, "require": { @@ -2046,9 +2046,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/feat-custom-queue-names" + "source": "https://github.com/utopia-php/platform/tree/integrate-workers" }, - "time": "2023-10-12T04:31:27+00:00" + "time": "2023-10-16T16:56:37+00:00" }, { "name": "utopia-php/pools", @@ -2160,12 +2160,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7" + "reference": "c6fc4a267ab643500442e0cffcdf952ed80161f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/5f81095e0ec66b2902037181ba3a20f6f9541af7", - "reference": "5f81095e0ec66b2902037181ba3a20f6f9541af7", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/c6fc4a267ab643500442e0cffcdf952ed80161f4", + "reference": "c6fc4a267ab643500442e0cffcdf952ed80161f4", "shasum": "" }, "require": { @@ -2213,7 +2213,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-get-worker-start" }, - "time": "2023-10-04T14:49:13+00:00" + "time": "2023-10-16T16:02:43+00:00" }, { "name": "utopia-php/registry", @@ -2269,16 +2269,16 @@ }, { "name": "utopia-php/storage", - "version": "0.14.0", + "version": "0.17.0", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c" + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c", - "reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/efec5376c02d3d8330f1beb1469e6d6e313e21ee", + "reference": "efec5376c02d3d8330f1beb1469e6d6e313e21ee", "shasum": "" }, "require": { @@ -2286,10 +2286,12 @@ "ext-fileinfo": "*", "ext-lz4": "*", "ext-snappy": "*", + "ext-xz": "*", "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2316,9 +2318,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.14.0" + "source": "https://github.com/utopia-php/storage/tree/0.17.0" }, - "time": "2023-03-15T00:16:34+00:00" + "time": "2023-08-21T11:28:36+00:00" }, { "name": "utopia-php/swoole", @@ -5314,5 +5316,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 9a2caf9c4e..94ba333e9a 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -71,7 +71,6 @@ class Databases extends Action DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForConsole, $dbForProject), DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForConsole, $dbForProject), DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForConsole, $dbForProject), - DATABASE_TYPE_DELETE_DOCUMENT => $this->deleteDocument($database, $collection, $document, $project, $dbForProject), default => Console::error('No database operation for type: ' . $type), }; } @@ -532,18 +531,6 @@ class Databases extends Action $this->deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project, $dbForProject); } - private function deleteDocument(Document $database, Document $collection, Document $document, Document $project, Database $dbForProject) - { - if ($collection->isEmpty()) { - throw new Exception('Missing collection'); - } - if ($document->isEmpty()) { - throw new Exception('Missing document'); - } - - $this->deleteAuditLogsByResource('document/' . $document->getId(), $project, $dbForProject); - } - /** * @param string $resource * @param Document $project From 133cb62b4ef76c06b24e675c63f661d6df9ded54 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:07:17 +0000 Subject: [PATCH 138/144] fix: update injection name --- app/controllers/api/databases.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 98edeeaa91..d132273b5d 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3524,10 +3524,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') - ->inject('deletes') + ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $deletes, Event $queueForEvents, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) { $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -3598,7 +3598,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $processDocument($collection, $document); - $deletes + $queueForDeletes ->setType(DELETE_TYPE_AUDIT) ->setDocument($document); From 74e3f3f7d2009559521a34d142a0dc2e1b74ff16 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:08:21 +0000 Subject: [PATCH 139/144] fix: remove unused variable --- app/init.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/init.php b/app/init.php index d6ce065250..df9e6be990 100644 --- a/app/init.php +++ b/app/init.php @@ -147,7 +147,6 @@ const DATABASE_TYPE_CREATE_INDEX = 'createIndex'; const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute'; const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex'; const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection'; -const DATABASE_TYPE_DELETE_DOCUMENT = 'deleteDocument'; const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase'; // Build Worker Types const BUILD_TYPE_DEPLOYMENT = 'deployment'; From 80086e9edcaf3fc453af5fe65604df161911078e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:26:35 +0000 Subject: [PATCH 140/144] chore: fix composer dependencies --- composer.lock | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/composer.lock b/composer.lock index 249723f44b..2751ee2098 100644 --- a/composer.lock +++ b/composer.lock @@ -2463,22 +2463,23 @@ }, { "name": "utopia-php/logger", - "version": "0.3.1", + "version": "0.3.2", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" + "reference": "9151b7d16eab18d4c37c34643041cc0f33ca4a6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", - "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/9151b7d16eab18d4c37c34643041cc0f33ca4a6c", + "reference": "9151b7d16eab18d4c37c34643041cc0f33ca4a6c", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "1.2.*", "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3", "vimeo/psalm": "4.0.1" @@ -2510,22 +2511,22 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.1" + "source": "https://github.com/utopia-php/logger/tree/0.3.2" }, - "time": "2023-02-10T15:52:50+00:00" + "time": "2023-10-16T08:16:19+00:00" }, { "name": "utopia-php/messaging", - "version": "0.2.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43" + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43", - "reference": "2d0f474a106bb1da285f85e105c29b46085d3a43", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", "shasum": "" }, "require": { @@ -2534,8 +2535,8 @@ }, "require-dev": { "laravel/pint": "^1.2", - "phpmailer/phpmailer": "6.8.*", - "phpunit/phpunit": "9.6.*" + "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "9.5.*" }, "type": "library", "autoload": { @@ -2558,9 +2559,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.2.0" + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" }, - "time": "2023-09-14T20:48:42+00:00" + "time": "2023-02-07T05:42:46+00:00" }, { "name": "utopia-php/migration", @@ -6019,5 +6020,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } From be26e4b729a46621b053515623e9b828c2593bdb Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:44:06 +0000 Subject: [PATCH 141/144] chore: remove resque library and update health check endpoints --- CONTRIBUTING.md | 2 - app/controllers/api/health.php | 68 +++--- app/init.php | 8 - composer.json | 1 - composer.lock | 232 +++++--------------- docker-compose.yml | 14 -- src/Appwrite/Event/Database.php | 1 - src/Appwrite/Platform/Tasks/Maintenance.php | 2 +- src/Appwrite/Platform/Workers/Databases.php | 1 + tests/resources/docker/docker-compose.yml | 14 -- 10 files changed, 93 insertions(+), 250 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e71fce135c..145ace7876 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -206,7 +206,6 @@ Appwrite's current structure is a combination of both [Monolithic](https://en.wi │ ├── Network │ ├── OpenSSL │ ├── Promises -│ ├── Resque │ ├── Specification │ ├── Task │ ├── Template @@ -251,7 +250,6 @@ Appwrite stack is a combination of a variety of open-source technologies and too - Imagemagick - for manipulating and managing image media files. - Webp - for better compression of images on supporting clients. - SMTP - for sending email messages and alerts. -- Resque - for managing data queues and scheduled tasks over a Redis server. ## Package Managers diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 2b41e2fef8..1051948629 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -14,6 +14,7 @@ use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; +use Utopia\Validator\Text; App::get('/v1/health') ->desc('Get HTTP') @@ -347,10 +348,11 @@ App::get('/v1/health/queue/webhooks') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::WEBHOOK_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::WEBHOOK_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/logs') @@ -364,10 +366,11 @@ App::get('/v1/health/queue/logs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::AUDITS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/certificates') @@ -381,10 +384,11 @@ App::get('/v1/health/queue/certificates') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::CERTIFICATES_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/builds') @@ -398,13 +402,14 @@ App::get('/v1/health/queue/builds') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::BUILDS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::BUILDS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/databases') +App::get('/v1/health/queue/databases/:databaseId') ->desc('Get databases queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -415,10 +420,13 @@ App::get('/v1/health/queue/databases') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->param('databaseId', 'database_db_main', new Text(256), 'Database for which to check the queue size', true) + ->inject('databaseId') + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::DATABASE_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (string $databaseId, Connection $queue, Response $response) { + $client = new Client($databaseId, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/deletes') @@ -432,10 +440,11 @@ App::get('/v1/health/queue/deletes') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::DELETE_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::DELETE_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/mails') @@ -449,10 +458,11 @@ App::get('/v1/health/queue/mails') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::MAILS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::MAILS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/messaging') @@ -466,10 +476,11 @@ App::get('/v1/health/queue/messaging') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::MESSAGING_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::MESSAGING_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/migrations') @@ -483,10 +494,11 @@ App::get('/v1/health/queue/migrations') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) + ->inject('queue') ->inject('response') - ->action(function (Response $response) { - - $response->dynamic(new Document([ 'size' => Resque::size(Event::MIGRATIONS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); + ->action(function (Connection $queue, Response $response) { + $client = new Client(Event::MIGRATIONS_QUEUE_NAME, $queue); + $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/functions') diff --git a/app/init.php b/app/init.php index df9e6be990..8aec6122fa 100644 --- a/app/init.php +++ b/app/init.php @@ -262,14 +262,6 @@ Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); -$user = App::getEnv('_APP_REDIS_USER', ''); -$pass = App::getEnv('_APP_REDIS_PASS', ''); -if (!empty($user) || !empty($pass)) { - Resque::setBackend('redis://' . $user . ':' . $pass . '@' . App::getEnv('_APP_REDIS_HOST', '') . ':' . App::getEnv('_APP_REDIS_PORT', '')); -} else { - Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '') . ':' . App::getEnv('_APP_REDIS_PORT', '')); -} - /** * New DB Filters */ diff --git a/composer.json b/composer.json index bd5b143f16..0ffbfe6200 100644 --- a/composer.json +++ b/composer.json @@ -68,7 +68,6 @@ "utopia-php/swoole": "0.5.*", "utopia-php/vcs": "0.5.*", "utopia-php/websocket": "0.1.*", - "resque/php-resque": "1.3.6", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", "influxdb/influxdb-php": "1.15.2", diff --git a/composer.lock b/composer.lock index 2751ee2098..e45e4b3d86 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": "5060c891e467051fedd25f3853cd37b0", + "content-hash": "3a0624bf1df70e602233efa5916aa6ce", "packages": [ { "name": "adhocore/jwt", @@ -339,53 +339,6 @@ ], "time": "2022-07-05T22:32:14+00:00" }, - { - "name": "colinmollenhour/credis", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/colinmollenhour/credis.git", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", - "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "suggest": { - "ext-redis": "Improved performance for communicating with redis" - }, - "type": "library", - "autoload": { - "classmap": [ - "Client.php", - "Cluster.php", - "Sentinel.php", - "Module.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin Mollenhour", - "email": "colin@mollenhour.com" - } - ], - "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", - "homepage": "https://github.com/colinmollenhour/credis", - "support": { - "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" - }, - "time": "2023-04-18T15:34:23+00:00" - }, { "name": "dragonmantank/cron-expression", "version": "v3.3.2", @@ -1476,56 +1429,6 @@ }, "time": "2023-04-04T09:54:51+00:00" }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -1570,89 +1473,6 @@ }, "time": "2019-03-08T08:55:37+00:00" }, - { - "name": "resque/php-resque", - "version": "v1.3.6", - "source": { - "type": "git", - "url": "https://github.com/resque/php-resque.git", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/resque/php-resque/zipball/fe41c04763699b1318d97ed14cc78583e9380161", - "reference": "fe41c04763699b1318d97ed14cc78583e9380161", - "shasum": "" - }, - "require": { - "colinmollenhour/credis": "~1.7", - "php": ">=5.6.0", - "psr/log": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "suggest": { - "ext-pcntl": "REQUIRED for forking processes on platforms that support it (so anything but Windows).", - "ext-proctitle": "Allows php-resque to rename the title of UNIX processes to show the status of a worker.", - "ext-redis": "Native PHP extension for Redis connectivity. Credis will automatically utilize when available." - }, - "bin": [ - "bin/resque", - "bin/resque-scheduler" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Resque": "lib", - "ResqueScheduler": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dan Hunsaker", - "email": "danhunsaker+resque@gmail.com", - "role": "Maintainer" - }, - { - "name": "Rajib Ahmed", - "homepage": "https://github.com/rajibahmed", - "role": "Maintainer" - }, - { - "name": "Steve Klabnik", - "email": "steve@steveklabnik.com", - "role": "Maintainer" - }, - { - "name": "Chris Boulton", - "email": "chris@bigcommerce.com", - "role": "Creator" - } - ], - "description": "Redis backed library for creating background jobs and processing them later. Based on resque for Ruby.", - "homepage": "http://www.github.com/resque/php-resque/", - "keywords": [ - "background", - "job", - "redis", - "resque" - ], - "support": { - "issues": "https://github.com/resque/php-resque/issues", - "source": "https://github.com/resque/php-resque/tree/v1.3.6" - }, - "time": "2020-04-16T16:39:50+00:00" - }, { "name": "slickdeals/statsd", "version": "3.1.0", @@ -4605,6 +4425,56 @@ ], "time": "2022-04-01T12:37:26+00:00" }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, { "name": "sebastian/cli-parser", "version": "1.0.1", diff --git a/docker-compose.yml b/docker-compose.yml index 78feecd17e..728e87cbc6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -872,7 +872,6 @@ services: # MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI. # RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks # RedisCommander - A nice UI for exploring Redis data - # Resque - A nice UI for exploring Redis pub/sub, view the different queues workloads, pending and failed tasks # Chronograf - A nice UI for exploring InfluxDB data # Webgrind - A nice UI for exploring and debugging code-level stuff @@ -914,19 +913,6 @@ services: # ports: # - "8081:8081" - # resque: - # image: appwrite/resque-web:1.1.0 - # restart: unless-stopped - # networks: - # - appwrite - # ports: - # - "5678:5678" - # environment: - # - RESQUE_WEB_HOST=redis - # - RESQUE_WEB_PORT=6379 - # - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user - # - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password - # chronograf: # image: chronograf:1.6 # container_name: appwrite-chronograf diff --git a/src/Appwrite/Event/Database.php b/src/Appwrite/Event/Database.php index e5a86ef840..442cbe4bbc 100644 --- a/src/Appwrite/Event/Database.php +++ b/src/Appwrite/Event/Database.php @@ -107,7 +107,6 @@ class Database extends Event */ public function trigger(): string|bool { - $this->setQueue($this->getProject()->getAttribute('database')); $client = new Client($this->queue, $this->connection); diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index f4756cd8a8..82a62ffed1 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -22,7 +22,7 @@ class Maintenance extends Action public function __construct() { $this - ->desc('Schedules maintenance tasks and publishes them to resque') + ->desc('Schedules maintenance tasks and publishes them to our queues') ->inject('dbForConsole') ->inject('queueForCertificates') ->inject('queueForDeletes') diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 7d0e015270..e0ec75e1d4 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -104,6 +104,7 @@ class Databases extends Action 'attributeId' => $attribute->getId() ]); /** + * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing informations. */ $attribute = $dbForProject->getDocument('attributes', $attribute->getId()); diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index bdb9bf49a4..50635018db 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -376,20 +376,6 @@ services: # ports: # - "8081:8081" - # resque: - # image: registry.gitlab.com/appwrite/appwrite/resque-web:v1.0.2 - # restart: unless-stopped - # networks: - # - appwrite - # ports: - # - "5678:5678" - # environment: - # - RESQUE_WEB_HOST=redis - # - RESQUE_WEB_PORT=6379 - # - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user - # - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password - - # webgrind: # image: 'jokkedk/webgrind:latest' # volumes: From 3d7cd91176e87a42fc687d1eeb2845b183c7b197 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 19:54:56 +0000 Subject: [PATCH 142/144] chore: fix injections --- app/controllers/api/health.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 1051948629..124166543c 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -421,7 +421,6 @@ App::get('/v1/health/queue/databases/:databaseId') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) ->param('databaseId', 'database_db_main', new Text(256), 'Database for which to check the queue size', true) - ->inject('databaseId') ->inject('queue') ->inject('response') ->action(function (string $databaseId, Connection $queue, Response $response) { From 6d55b20faca664b19e1d6b0d1f7a481b38d054d0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 20:18:15 +0000 Subject: [PATCH 143/144] chore: update health tests --- tests/e2e/Services/Health/HealthCustomServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 4ac15bd95c..c923491942 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -214,7 +214,7 @@ class HealthCustomServerTest extends Scope /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/databases', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/databases/database_db_main', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); From 67541d4a1dbeb1da12ef9b8d6af151385a21a228 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 18 Oct 2023 21:35:15 +0000 Subject: [PATCH 144/144] chore: review comments --- app/cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cli.php b/app/cli.php index a24e4c386e..5b5ae47bf6 100644 --- a/app/cli.php +++ b/app/cli.php @@ -146,7 +146,7 @@ CLI::setResource('queue', function (Group $pools) { }, ['pools']); CLI::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); -}, ['pools']); +}, ['queue']); CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']);