From ca46e27e8a94040d470d38a31ee29eaab1d69ca0 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 4 Sep 2023 16:52:19 +0300 Subject: [PATCH] new usage integration --- .env | 4 - CHANGES.md | 35 + CONTRIBUTING.md | 2 - Dockerfile | 17 +- app/cli.php | 23 - app/config/collections.php | 56 +- app/controllers/api/account.php | 29 - app/controllers/api/databases.php | 492 +- app/controllers/api/functions.php | 294 +- app/controllers/api/project.php | 143 +- app/controllers/api/storage.php | 282 +- app/controllers/api/users.php | 1 - app/controllers/shared/api.php | 201 +- app/init.php | 33 +- app/views/install/compose.phtml | 82 +- app/worker.php | 15 +- app/workers/builds.php | 28 +- app/workers/functions.php | 46 +- app/workers/usage.php | 288 + bin/worker-usage | 3 + composer.json | 1 - composer.lock | 6100 ----------------- docker-compose.yml | 64 +- 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 - 28 files changed, 1015 insertions(+), 8083 deletions(-) create mode 100644 app/workers/usage.php create mode 100644 bin/worker-usage 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/.env b/.env index 6ef35b57c1..12ed7ead70 100644 --- a/.env +++ b/.env @@ -48,10 +48,6 @@ _APP_STORAGE_WASABI_BUCKET= _APP_STORAGE_ANTIVIRUS=disabled _APP_STORAGE_ANTIVIRUS_HOST=clamav _APP_STORAGE_ANTIVIRUS_PORT=3310 -_APP_INFLUXDB_HOST=influxdb -_APP_INFLUXDB_PORT=8086 -_APP_STATSD_HOST=telegraf -_APP_STATSD_PORT=8125 _APP_SMTP_HOST=maildev _APP_SMTP_PORT=1025 _APP_SMTP_SECURE= diff --git a/CHANGES.md b/CHANGES.md index 2d9af087db..2d1a667244 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -137,6 +137,41 @@ - Get default region from environment on project create [#4780](https://github.com/appwrite/appwrite/pull/4780) - Fix french translation [#4782](https://github.com/appwrite/appwrite/pull/4782) - Fix max mimetype size [#4814](https://github.com/appwrite/appwrite/pull/4814) +- New usage metrics collection flow [#4770](https://github.com/appwrite/appwrite/pull/4770) + - Deprecated influxdb, telegraf containers and removed all of their occurrences from the code. + - Removed _APP_INFLUXDB_HOST, _APP_INFLUXDB_PORT, _APP_STATSD_HOST, _APP_STATSD_PORT env variables. + - Removed usage labels dependency. + - Dropped type attribute from stats collection. + - Usage metrics are processed via new usage worker. + - Metrics changes: + - Storage + - deprecated + - filesCreate, filesRead, filesUpdate, filesDelete, bucketsCreate, bucketsRead, bucketsUpdate, bucketsDelete. + - renamed + - filesCount to filesTotal, storage to filesStorage, bucketsCount to bucketsTotal. + - Auth + - deprecated + - usersCreate, usersRead, usersUpdate, usersDelete, sessionsCreate sessionsProviderCreate, sessionsDelete. + - renamed + - usersCount to usersTotal. + - added + - sessionsTotal. + - Databases + - deprecated + - databasesCreate, databasesRead, databasesDelete, documentsCreate, documentsRead, documentsUpdate, documentsDelete, collectionsCreate, collectionsRead, collectionsUpdate, collectionsDelete. + - renamed + - databasesCount to databasesTotal, collectionsCount to collectionsTotal, documentsCount to documentsTotal. + - Functions + - deprecated + - executionsFailure, executionsSuccess, buildsFailure, buildsSuccess, executionsFailure, executionsSuccess. + - renamed + - executionsTime to executionsCompute, buildsTime to buildsCompute, documentsCount to documentsTotal. + - added + - functionsTotal, buildsStorage, deploymentsTotal, deploymentsStorage. + - Project + - renamed + - executions to executionsTotal, builds to buildsTotal, requests to requestsTotal, storage to filesStorage, buckets to bucketsTotal, users to usersTotal, documents to documentsTotal, collections to collectionsTotal, databases to databasesTotal. + ## Bugs - Fix invited account verified status [#4776](https://github.com/appwrite/appwrite/pull/4776) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1341c9f133..df5f0e6262 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,8 +240,6 @@ Appwrite stack is a combination of a variety of open-source technologies and too - Redis - for managing cache and in-memory data (currently, we do not use Redis for persistent data). - MariaDB - for database storage and queries. -- InfluxDB - for managing stats and time-series based data -- Statsd - for sending data over UDP protocol (using Telegraf) - ClamAV - for validating and scanning storage files. - Imagemagick - for manipulating and managing image media files. - Webp - for better compression of images on supporting clients. diff --git a/Dockerfile b/Dockerfile index 23bd3f188f..602ddac29b 100755 --- a/Dockerfile +++ b/Dockerfile @@ -91,10 +91,6 @@ ENV _APP_SERVER=swoole \ _APP_DB_USER=root \ _APP_DB_PASS=password \ _APP_DB_SCHEMA=appwrite \ - _APP_INFLUXDB_HOST=influxdb \ - _APP_INFLUXDB_PORT=8086 \ - _APP_STATSD_HOST=telegraf \ - _APP_STATSD_PORT=8125 \ _APP_SMTP_HOST= \ _APP_SMTP_PORT= \ _APP_SMTP_SECURE= \ @@ -165,7 +161,6 @@ RUN mkdir -p /storage/uploads && \ # Executables RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/maintenance && \ - chmod +x /usr/local/bin/usage && \ chmod +x /usr/local/bin/install && \ chmod +x /usr/local/bin/upgrade && \ chmod +x /usr/local/bin/migrate && \ @@ -185,16 +180,8 @@ 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-migrations - -# Cloud Executabless -RUN chmod +x /usr/local/bin/hamster && \ - chmod +x /usr/local/bin/volume-sync && \ - chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \ - chmod +x /usr/local/bin/patch-delete-project-collections && \ - chmod +x /usr/local/bin/clear-card-cache && \ - chmod +x /usr/local/bin/calc-users-stats && \ - chmod +x /usr/local/bin/calc-tier-stats + chmod +x /usr/local/bin/worker-migrations && \ + chmod +x /usr/local/bin/worker-usage # Letsencrypt Permissions RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/ diff --git a/app/cli.php b/app/cli.php index c8ebd089b1..b59245d4ae 100644 --- a/app/cli.php +++ b/app/cli.php @@ -116,29 +116,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('queueForFunctions', function (Group $pools) { return new Func($pools->get('queue')->pop()->getResource()); diff --git a/app/config/collections.php b/app/config/collections.php index 1d452f4b3a..7826d51306 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1302,7 +1302,7 @@ $commonCollections = [ 'type' => Database::VAR_INTEGER, 'format' => '', 'size' => 8, - 'signed' => false, + 'signed' => true, 'required' => true, 'default' => null, 'array' => false, @@ -1330,17 +1330,6 @@ $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' => [ [ @@ -1359,14 +1348,53 @@ $commonCollections = [ ], [ '$id' => ID::custom('_key_metric_period_time'), - 'type' => Database::INDEX_KEY, + 'type' => Database::INDEX_UNIQUE, 'attributes' => ['metric', 'period', 'time'], 'lengths' => [], 'orders' => [Database::ORDER_DESC], ], ], ], - ]; + + 'statsLogger' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('statsLogger'), + 'name' => 'StatsLogger', + 'attributes' => [ + [ + '$id' => ID::custom('time'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('metrics'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 5012, + 'signed' => true, + 'required' => false, + 'default' => [], + 'array' => false, + 'filters' => ['json'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_time'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['time'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + ], + ], +]; $projectCollections = array_merge([ 'databases' => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index c6df38c3ca..a919bcba5d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -58,7 +58,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') @@ -172,8 +171,6 @@ 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') @@ -426,8 +423,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, 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) @@ -1094,8 +1089,6 @@ 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') @@ -1468,8 +1461,6 @@ 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') @@ -1646,7 +1637,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') @@ -1667,7 +1657,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') @@ -1690,7 +1679,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') @@ -1729,7 +1717,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') @@ -1790,7 +1777,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') @@ -1838,7 +1824,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') @@ -1873,7 +1858,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') @@ -1939,7 +1923,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') @@ -2009,7 +1992,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') @@ -2068,7 +2050,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') @@ -2102,7 +2083,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') @@ -2146,7 +2126,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') @@ -2223,7 +2202,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') @@ -2308,7 +2286,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') @@ -2370,7 +2347,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') @@ -2553,7 +2529,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') @@ -2640,7 +2615,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') @@ -2801,7 +2775,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') @@ -2862,7 +2835,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') @@ -2958,7 +2930,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/databases.php b/app/controllers/api/databases.php index bf66e9fd8e..6d18ee7265 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -377,7 +377,6 @@ 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') @@ -451,7 +450,6 @@ 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') @@ -499,7 +497,6 @@ 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') @@ -614,7 +611,6 @@ 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') @@ -659,7 +655,6 @@ 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') @@ -704,8 +699,6 @@ 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') @@ -771,8 +764,6 @@ 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') @@ -830,8 +821,6 @@ 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') @@ -866,8 +855,6 @@ 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') @@ -965,8 +952,6 @@ 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') @@ -1035,8 +1020,6 @@ 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') @@ -1091,8 +1074,6 @@ 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') @@ -1149,8 +1130,6 @@ 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') @@ -1193,8 +1172,6 @@ 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') @@ -1253,8 +1230,6 @@ 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') @@ -1297,8 +1272,6 @@ 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') @@ -1341,8 +1314,6 @@ 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') @@ -1414,8 +1385,6 @@ 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') @@ -1490,8 +1459,6 @@ 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') @@ -1533,8 +1500,6 @@ 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') @@ -1579,8 +1544,6 @@ 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') @@ -1658,8 +1621,6 @@ 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') @@ -1733,8 +1694,6 @@ 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') @@ -1812,8 +1771,6 @@ 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') @@ -1853,8 +1810,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email ->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', 'updateEmailAttribute') @@ -1894,8 +1849,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/ ->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', 'updateEnumAttribute') @@ -1937,8 +1890,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k ->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', 'updateIpAttribute') @@ -1978,8 +1929,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/: ->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', 'updateUrlAttribute') @@ -2019,8 +1968,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->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', 'updateIntegerAttribute') @@ -2070,8 +2017,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->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', 'updateFloatAttribute') @@ -2121,8 +2066,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole ->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', 'updateBooleanAttribute') @@ -2161,8 +2104,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet ->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', 'documents.{scope}.requests.update') - ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateDatetimeAttribute') @@ -2201,8 +2142,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ ->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', 'documents.{scope}.requests.update') - ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateRelationshipAttribute') @@ -2257,8 +2196,6 @@ 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') @@ -2368,8 +2305,6 @@ 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') @@ -2525,8 +2460,6 @@ 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') @@ -2590,8 +2523,6 @@ 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') @@ -2634,8 +2565,6 @@ 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') @@ -2700,8 +2629,6 @@ 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) @@ -2937,8 +2864,6 @@ 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') @@ -3064,8 +2989,6 @@ 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') @@ -3159,8 +3082,6 @@ 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') @@ -3263,8 +3184,6 @@ 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) @@ -3492,8 +3411,6 @@ 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) @@ -3602,7 +3519,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu App::get('/v1/databases/usage') ->desc('Get usage stats for the database') - ->groups(['api', 'database']) + ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') @@ -3615,112 +3532,62 @@ App::get('/v1/databases/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_DATABASES, + METRIC_COLLECTIONS, + METRIC_DOCUMENTS, + ]; - $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]); + 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, - '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'] ?? [], - ]); + $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_DATABASES); + } + $response->dynamic(new Document([ + 'range' => $range, + 'databasesTotal' => $usage[$metrics[0]], + 'collectionsTotal' => $usage[$metrics[1]], + 'documentsTotal' => $usage[$metrics[2]], + ]), Response::MODEL_USAGE_DATABASES); }); App::get('/v1/databases/:databaseId/usage') ->desc('Get usage stats for the database') - ->groups(['api', 'database']) + ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') @@ -3734,103 +3601,68 @@ App::get('/v1/databases/:databaseId/usage') ->inject('dbForProject') ->action(function (string $databaseId, 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, - ], - ]; + $database = $dbForProject->getDocument('databases', $databaseId); - $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"] ?? [], - ]); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); } - $response->dynamic($usage, Response::MODEL_USAGE_DATABASE); + $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, + ]; + } + } + + $response->dynamic(new Document([ + 'range' => $range, + 'collectionsTotal' => $usage[$metrics[0]], + 'documentsTotal' => $usage[$metrics[1]], + ]), Response::MODEL_USAGE_DATABASE); }); App::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default']) ->desc('Get usage stats for a collection') - ->groups(['api', 'database']) + ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') @@ -3853,84 +3685,52 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') throw new Exception(Exception::COLLECTION_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(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), + ]; - $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]); + 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, - '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" ?? []] - ]); + $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_COLLECTION); + $response->dynamic(new Document([ + 'range' => $range, + 'documentsTotal' => $usage[$metrics[0]], + ]), Response::MODEL_USAGE_COLLECTION); }); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index a286b4f567..ad12e6abf1 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -453,92 +453,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') @@ -556,92 +530,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') @@ -1506,11 +1455,11 @@ App::post('/v1/functions/:functionId/executions') ->inject('dbForProject') ->inject('user') ->inject('events') - ->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 $events, Stats $usage, string $mode, Func $queueForFunctions, Reader $geodb) { + ->inject('queueForUsage') + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, string $mode, Func $queueForFunctions, Reader $geodb, Usage $queueForUsage) { $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); @@ -1714,6 +1663,13 @@ App::post('/v1/functions/:functionId/executions') $execution->setAttribute('logs', $executionResponse['logs']); $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); @@ -1730,14 +1686,6 @@ App::post('/v1/functions/:functionId/executions') $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 0f0a296185..1d5b03c619 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -14,11 +14,10 @@ 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']) + ->groups(['api', 'usage']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'project') @@ -30,94 +29,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/storage.php b/app/controllers/api/storage.php index 8cad0aaf8f..d9929e9fad 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1,7 +1,6 @@ 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') @@ -148,7 +146,6 @@ 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') @@ -197,7 +194,6 @@ 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') @@ -226,7 +222,6 @@ 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') @@ -294,7 +289,6 @@ 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') @@ -337,8 +331,6 @@ 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) @@ -688,8 +680,6 @@ 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') @@ -769,8 +759,6 @@ 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') @@ -820,8 +808,6 @@ 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') @@ -986,8 +972,6 @@ 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') @@ -1129,8 +1113,6 @@ 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') @@ -1285,8 +1267,6 @@ 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) @@ -1394,8 +1374,6 @@ 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) @@ -1500,103 +1478,62 @@ App::get('/v1/storage/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_BUCKETS, + METRIC_FILES, + METRIC_FILES_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]); + 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, - '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'], - ]); + $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_STORAGE); + $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 a storage bucket') + ->desc('Get usage stats for storage bucket') ->groups(['api', 'storage', 'usage']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -1617,86 +1554,55 @@ App::get('/v1/storage/:bucketId/usage') throw new Exception(Exception::STORAGE_BUCKET_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('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES), + str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), + ]; - $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]); + 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, - 'filesCount' => $stats[$metrics[0]], - 'filesStorage' => $stats[$metrics[1]], - 'filesCreate' => $stats[$metrics[2]], - 'filesRead' => $stats[$metrics[3]], - 'filesUpdate' => $stats[$metrics[4]], - 'filesDelete' => $stats[$metrics[5]], - ]); + $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_BUCKETS); + $response->dynamic(new Document([ + 'range' => $range, + 'filesTotal' => $usage[$metrics[0]], + 'filesStorage' => $usage[$metrics[1]], + ]), Response::MODEL_USAGE_BUCKETS); }); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index c6d9b2d4e3..0428c39d7f 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -115,7 +115,6 @@ 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') diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 9dd800b69b..51d8f9c025 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -8,6 +8,7 @@ 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; @@ -48,43 +49,99 @@ $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 + $queueForUsage + ->addMetric(METRIC_FILES, $value) // per project + ->addMetric(METRIC_FILES_STORAGE, $document->getAttribute('sizeOriginal') * $value) // per project + ->addMetric(str_replace('{bucketInternalId}', $document->getAttribute('bucketInternalId'), METRIC_BUCKET_ID_FILES), $value) // per bucket + ->addMetric(str_replace('{bucketInternalId}', $document->getAttribute('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; } }; @@ -101,10 +158,10 @@ App::init() ->inject('deletes') ->inject('database') ->inject('dbForProject') + ->inject('queueForUsage') ->inject('mode') ->inject('mails') - ->inject('usage') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, string $mode, Mail $mails, Stats $usage) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, Usage $queueForUsage, string $mode, Mail $mails) use ($databaseListener) { $route = $utopia->getRoute(); @@ -186,19 +243,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); - $deletes->setProject($project); $database->setProject($project); - $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)); + $calculateUsage = fn ($event, Document $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject); + + $dbForProject + ->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', $calculateUsage) + ->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', $calculateUsage); $useCache = $route->getLabel('cache', false); @@ -364,14 +416,14 @@ App::shutdown() ->inject('user') ->inject('events') ->inject('audits') - ->inject('usage') ->inject('deletes') ->inject('database') ->inject('dbForProject') ->inject('queueForFunctions') + ->inject('queueForUsage') ->inject('mode') ->inject('dbForConsole') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Stats $usage, Delete $deletes, EventDatabase $database, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Delete $deletes, EventDatabase $database, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage, string $mode, Database $dbForConsole) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -524,35 +576,46 @@ 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(); + } + + /** + * Update user last activity + */ + if (!$user->isEmpty()) { + $accessedAt = $user->getAttribute('accessedAt', ''); + if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCCESS)) > $accessedAt) { + $user->setAttribute('accessedAt', DateTime::now()); + + if (APP_MODE_ADMIN !== $mode) { + $dbForProject->updateDocument('users', $user->getId(), $user); + } else { + $dbForConsole->updateDocument('users', $user->getId(), $user); } } + } + }); - $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(); +App::init() + ->groups(['usage']) + ->action(function () { + if (App::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') { + throw new Exception(Exception::GENERAL_USAGE_DISABLED); } }); diff --git a/app/init.php b/app/init.php index 439cef7a80..9628711a28 100644 --- a/app/init.php +++ b/app/init.php @@ -32,7 +32,6 @@ 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; @@ -237,6 +236,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 @@ -771,31 +771,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); @@ -899,9 +874,9 @@ App::setResource('queue', function (Group $pools) { App::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -App::setResource('usage', function ($register) { - return new Stats($register->get('statsd')); -}, ['register']); +App::setResource('queueForUsage', function (Connection $queue) { + return new Usage($queue); +}, ['queue']); 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 4cbfc2fd1a..5d87bb27c4 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -71,7 +71,6 @@ services: - mariadb - redis # - clamav - - influxdb environment: - _APP_ENV - _APP_WORKER_PER_CORE @@ -104,8 +103,6 @@ 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 @@ -142,8 +139,6 @@ 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 @@ -534,37 +529,6 @@ services: - _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - - _APP_MAINTENANCE_RETENTION_SCHEDULES - - appwrite-usage: - image: /: - entrypoint: usage - container_name: appwrite-usage - <<: *x-logging - restart: unless-stopped - networks: - - appwrite - depends_on: - - influxdb - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _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_LOGGING_PROVIDER - - _APP_LOGGING_CONFIG appwrite-schedule: image: /: @@ -676,26 +640,41 @@ services: # volumes: # - appwrite-uploads:/storage/uploads - influxdb: - image: appwrite/influxdb:1.5.0 - container_name: appwrite-influxdb + appwrite-worker-usage: + image: /: + entrypoint: worker-usage <<: *x-logging - restart: unless-stopped + container_name: appwrite-worker-usage networks: - appwrite volumes: - - appwrite-influxdb:/var/lib/influxdb:rw - - telegraf: - image: appwrite/telegraf:1.4.0 - container_name: appwrite-telegraf - <<: *x-logging - restart: unless-stopped - networks: - - appwrite + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb environment: - - _APP_INFLUXDB_HOST - - _APP_INFLUXDB_PORT + - _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_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 + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG networks: gateway: @@ -713,5 +692,4 @@ volumes: appwrite-certificates: appwrite-functions: appwrite-builds: - appwrite-influxdb: appwrite-config: diff --git a/app/worker.php b/app/worker.php index 51e42969f4..3698be6e20 100644 --- a/app/worker.php +++ b/app/worker.php @@ -4,7 +4,6 @@ require_once __DIR__ . '/init.php'; use Appwrite\Event\Func; use Appwrite\Event\Usage; -use Appwrite\Usage\Stats; use Swoole\Runtime; use Utopia\App; use Utopia\Cache\Adapter\Sharding; @@ -89,16 +88,22 @@ Server::setResource('queueForFunctions', function (Registry $register) { ); }, ['register']); +Server::setResource('queueForUsage', function (Registry $register) { + $pools = $register->get('pools'); + return new Usage( + $pools + ->get('queue') + ->pop() + ->getResource() + ); +}, ['register']); + Server::setResource('log', fn() => new Log()); 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']); diff --git a/app/workers/builds.php b/app/workers/builds.php index 41867a5dd3..73385a0cf2 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -490,23 +490,19 @@ class BuildsV1 extends Worker 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(); - } } + + /** Trigger usage queue */ + $this + ->getUsageQueue() + ->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(); } protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole): void diff --git a/app/workers/functions.php b/app/workers/functions.php index cae3a73cc8..2ac8097312 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -2,12 +2,11 @@ require_once __DIR__ . '/../worker.php'; -use Domnikl\Statsd\Client; +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 Executor\Executor; use Utopia\App; @@ -31,7 +30,7 @@ Server::setResource('execute', function () { Log $log, Func $queueForFunctions, Database $dbForProject, - Client $statsd, + Usage $queueForUsage, Document $project, Document $function, string $trigger, @@ -47,6 +46,7 @@ Server::setResource('execute', function () { ) { $user ??= new Document(); $functionId = $function->getId(); + $functionInternalId = $function->getInternalId(); $deploymentId = $function->getAttribute('deployment', ''); $log->addTag('functionId', $functionId); @@ -54,6 +54,7 @@ Server::setResource('execute', function () { /** 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'); @@ -127,6 +128,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 } if ($execution->getAttribute('status') !== 'processing') { @@ -265,20 +274,13 @@ 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() + ; if (!empty($error)) { throw new Exception($error, $errorCode); @@ -290,10 +292,10 @@ $server->job() ->inject('message') ->inject('dbForProject') ->inject('queueForFunctions') - ->inject('statsd') + ->inject('queueForUsage') ->inject('execute') ->inject('log') - ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Client $statsd, callable $execute, Log $log) { + ->action(function (Message $message, Database $dbForProject, Func $queueForFunctions, Usage $queueForUsage, callable $execute, Log $log) { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -335,7 +337,7 @@ $server->job() } Console::success('Iterating function: ' . $function->getAttribute('name')); $execute( - statsd: $statsd, + queueForUsage: $queueForUsage, dbForProject: $dbForProject, project: $project, function: $function, @@ -383,7 +385,7 @@ $server->job() path: $payload['path'], method: $payload['method'], headers: $payload['headers'], - statsd: $statsd, + queueForUsage: $queueForUsage, ); break; case 'schedule': @@ -403,7 +405,7 @@ $server->job() path: $payload['path'], method: $payload['method'], headers: $payload['headers'], - statsd: $statsd, + queueForUsage: $queueForUsage, ); break; } diff --git a/app/workers/usage.php b/app/workers/usage.php new file mode 100644 index 0000000000..0c9dcbc5aa --- /dev/null +++ b/app/workers/usage.php @@ -0,0 +1,288 @@ +deployments>build || documents>collection>database || buckets>files. + * When we remove a parent document we need to deduct his children aggregation from the project scope. + */ +Server::setResource('reduce', function (Cache $cache, Registry $register, $pools) { + return function ($database, $projectInternalId, Document $document, array &$metrics) use ($pools, $cache, $register): void { + 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(INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); + $documents = $dbForProject->getDocument('stats', md5(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(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(INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); + $storage = $dbForProject->getDocument('stats', md5(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(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS))); + $deploymentsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE))); + $builds = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS))); + $buildsStorage = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE))); + $buildsCompute = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE))); + $executions = $dbForProject->getDocument('stats', md5(INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS))); + $executionsCompute = $dbForProject->getDocument('stats', md5(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()}"); + } finally { + $pools->reclaim(); + } + }; +}, ['cache', 'register', 'pools']); + + +$server->job() + ->inject('message') + ->inject('reduce') + + ->action(function (Message $message, callable $reduce) use (&$stats) { + + $payload = $message->getPayload() ?? []; + $project = new Document($payload['project'] ?? []); + $projectId = $project->getInternalId(); + foreach ($payload['reduce'] ?? [] as $document) { + if (empty($document)) { + continue; + } + + $reduce( + database: $project->getAttribute('database'), + projectInternalId: $project->getInternalId(), + document: new Document($document), + metrics: $payload['metrics'], + ); + } + + $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']; + } + }); + +$server + ->workerStart() + ->inject('register') + ->inject('cache') + ->inject('pools') + ->action(function ($register, $cache, $pools) use ($periods, &$stats) { + Timer::tick(30000, function () use ($register, $cache, $pools, $periods, &$stats) { + + $offset = count($stats); + $projects = array_slice($stats, 0, $offset, true); + array_splice($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 ($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) { + $now = DateTime::now(); + console::error("[Error] " . " Time: {$now} " . " projectInternalId: {$projectInternalId}" . " File: {$e->getFile()}" . " Line: {$e->getLine()} " . " message: {$e->getMessage()}"); + } finally { + $pools->reclaim(); + } + } + }); + }); +$server->start(); diff --git a/bin/worker-usage b/bin/worker-usage new file mode 100644 index 0000000000..9d325ac46e --- /dev/null +++ b/bin/worker-usage @@ -0,0 +1,3 @@ +#!/bin/sh + +QUEUE=v1-usage php /usr/src/code/app/workers/usage.php $@ \ No newline at end of file diff --git a/composer.json b/composer.json index fba885f951..34793bbcab 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 8a3bf6d014..0000000000 --- a/composer.lock +++ /dev/null @@ -1,6100 +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": "bc47430e5cb3430f354b4eee6fd8c9c7", - "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": "8.0.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "2b9e966edf35c4061179ed98ea364698ab30de8b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/2b9e966edf35c4061179ed98ea364698ab30de8b", - "reference": "2b9e966edf35c4061179ed98ea364698ab30de8b", - "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/8.0.0", - "url": "https://appwrite.io/support" - }, - "time": "2023-04-12T10:16:28+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.12.0", - "source": { - "type": "git", - "url": "https://github.com/appwrite/runtimes.git", - "reference": "5aa672ae744be0d7a3d4bf4c93455c65e9a23b4f" - }, - "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-08-07T09:56:11+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": "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.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": "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": "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.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.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.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "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/tree/1.0.2" - }, - "time": "2023-04-10T20:12:12+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.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/database.git", - "reference": "fb96fc6c94d5efcd43913c34bece62daba76a5e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/fb96fc6c94d5efcd43913c34bece62daba76a5e9", - "reference": "fb96fc6c94d5efcd43913c34bece62daba76a5e9", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "ext-pdo": "*", - "php": ">=8.0", - "utopia-php/cache": "0.8.*", - "utopia-php/framework": "0.*.*", - "utopia-php/mongo": "0.2.*" - }, - "require-dev": { - "fakerphp/faker": "^1.14", - "laravel/pint": "1.4.*", - "mongodb/mongodb": "1.8.0", - "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.0" - }, - "time": "2023-08-29T10:18:39+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.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/migration.git", - "reference": "49a28adfbb781f0c08f1eaf459a8fbb8ab9fcc70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/49a28adfbb781f0c08f1eaf459a8fbb8ab9fcc70", - "reference": "49a28adfbb781f0c08f1eaf459a8fbb8ab9fcc70", - "shasum": "" - }, - "require": { - "appwrite/appwrite": "^8.0", - "php": ">=8.0", - "utopia-php/cli": "^0.15.0" - }, - "require-dev": { - "laravel/pint": "^1.10", - "phpunit/phpunit": "^9.3", - "vlucas/phpdotenv": "^5.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Migration\\": "src/Migration" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Bradley Schofield", - "email": "bradley@appwrite.io" - } - ], - "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.2" - }, - "time": "2023-08-31T04:11:35+00:00" - }, - { - "name": "utopia-php/mongo", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "b6dfb31b93c07c59b8bbd62a3b52e3b97a407c09" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/b6dfb31b93c07c59b8bbd62a3b52e3b97a407c09", - "reference": "b6dfb31b93c07c59b8bbd62a3b52e3b97a407c09", - "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.2.0" - }, - "time": "2023-03-22T10:44:29+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.4.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/platform.git", - "reference": "6e3d6db9ee8f99e36c2df331b58de2e6e3e37eb9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/6e3d6db9ee8f99e36c2df331b58de2e6e3e37eb9", - "reference": "6e3d6db9ee8f99e36c2df331b58de2e6e3e37eb9", - "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.4.2" - }, - "time": "2023-08-30T16:28:31+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.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.4.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/vcs.git", - "reference": "08078af30865827adb8aafb32929c968ce541a28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/08078af30865827adb8aafb32929c968ce541a28", - "reference": "08078af30865827adb8aafb32929c968ce541a28", - "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.4.0" - }, - "time": "2023-08-30T16:17:03+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.34.1", - "source": { - "type": "git", - "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "81538d10abacd81350c265b516c72ef315116013" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/81538d10abacd81350c265b516c72ef315116013", - "reference": "81538d10abacd81350c265b516c72ef315116013", - "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.34.1" - }, - "time": "2023-08-30T07:57:31+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.23.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "846ae76eef31c6d7790fac9bc399ecee45160b26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26", - "reference": "846ae76eef31c6d7790fac9bc399ecee45160b26", - "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.23.1" - }, - "time": "2023-08-03T16:32:59+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.27", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1", - "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.27" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-07-26T13:44:30+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.3.0" -} diff --git a/docker-compose.yml b/docker-compose.yml index b27c3581af..8ce381e6d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -125,8 +125,6 @@ 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 @@ -163,8 +161,6 @@ 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 @@ -620,36 +616,38 @@ services: - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES - appwrite-usage: - entrypoint: usage + appwrite-worker-usage: + entrypoint: worker-usage <<: *x-logging - container_name: appwrite-usage + container_name: appwrite-worker-usage image: appwrite-dev networks: - appwrite volumes: - ./app:/usr/src/code/app - ./src:/usr/src/code/src - - ./dev:/usr/local/dev depends_on: - - influxdb + - redis - 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 - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG @@ -812,26 +810,6 @@ 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 @@ -895,26 +873,6 @@ services: # - 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 - # 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: @@ -952,6 +910,4 @@ volumes: appwrite-certificates: appwrite-functions: appwrite-builds: - appwrite-influxdb: appwrite-config: - # appwrite-chronograf: 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; - } -}