Merge branch 'feat-db-pools-master' of https://github.com/appwrite/appwrite into feat-db-pools-sync
This commit is contained in:
commit
5f10ff4d3b
13 changed files with 742 additions and 392 deletions
3
.env
3
.env
|
@ -41,8 +41,9 @@ _APP_SMTP_PORT=1025
|
|||
_APP_SMTP_SECURE=
|
||||
_APP_SMTP_USERNAME=
|
||||
_APP_SMTP_PASSWORD=
|
||||
_APP_HAMSTER_RECIPIENTS=
|
||||
_APP_HAMSTER_INTERVAL=86400
|
||||
_APP_HAMSTER_TIME=21:00
|
||||
_APP_MIXPANEL_TOKEN=
|
||||
_APP_SMS_PROVIDER=sms://username:password@mock
|
||||
_APP_SMS_FROM=+123456789
|
||||
_APP_STORAGE_LIMIT=30000000
|
||||
|
|
|
@ -111,7 +111,7 @@ RUN mkdir -p /storage/uploads && \
|
|||
|
||||
# Executables
|
||||
RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/patch-create-missing-schedules && \
|
||||
chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
chmod +x /usr/local/bin/volume-sync && \
|
||||
chmod +x /usr/local/bin/install && \
|
||||
|
|
|
@ -2311,17 +2311,6 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('scheduleUpdatedAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('timeout'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
|
|
|
@ -85,8 +85,6 @@ App::post('/v1/functions')
|
|||
'deployment' => '',
|
||||
'events' => $events,
|
||||
'schedule' => $schedule,
|
||||
'scheduleInternalId' => '',
|
||||
'scheduleUpdatedAt' => DateTime::now(),
|
||||
'timeout' => $timeout,
|
||||
'search' => implode(' ', [$functionId, $name, $runtime])
|
||||
]));
|
||||
|
@ -416,7 +414,6 @@ App::put('/v1/functions/:functionId')
|
|||
'name' => $name,
|
||||
'events' => $events,
|
||||
'schedule' => $schedule,
|
||||
'scheduleUpdatedAt' => DateTime::now(),
|
||||
'timeout' => $timeout,
|
||||
'enabled' => $enabled,
|
||||
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
|
||||
|
@ -424,18 +421,11 @@ App::put('/v1/functions/:functionId')
|
|||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
|
||||
/**
|
||||
* In case we want to clear the schedule
|
||||
*/
|
||||
if (!empty($function->getAttribute('deployment'))) {
|
||||
$schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt'));
|
||||
}
|
||||
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
|
||||
|
||||
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
|
||||
$eventsInstance->setParam('functionId', $function->getId());
|
||||
|
@ -492,15 +482,10 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
])));
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
|
||||
$active = !empty($function->getAttribute('schedule'));
|
||||
|
||||
if ($active) {
|
||||
$schedule->setAttribute('resourceUpdatedAt', datetime::now());
|
||||
}
|
||||
|
||||
$schedule->setAttribute('active', $active);
|
||||
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
|
||||
$events
|
||||
|
@ -749,22 +734,6 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO Should we update also the function collection with the scheduleUpdatedAt attr?
|
||||
*/
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
|
||||
$active = !empty($function->getAttribute('schedule'));
|
||||
|
||||
if ($active) {
|
||||
$schedule->setAttribute('resourceUpdatedAt', datetime::now());
|
||||
}
|
||||
|
||||
$schedule->setAttribute('active', $active);
|
||||
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
|
||||
$metadata = null;
|
||||
|
||||
$events
|
||||
|
@ -1347,7 +1316,8 @@ App::post('/v1/functions/:functionId/variables')
|
|||
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject) {
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
|
@ -1376,6 +1346,13 @@ App::post('/v1/functions/:functionId/variables')
|
|||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
|
||||
$dbForProject->deleteCachedDocument('functions', $function->getId());
|
||||
|
||||
$response
|
||||
|
@ -1463,7 +1440,8 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject) {
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
@ -1492,6 +1470,13 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
|
||||
$dbForProject->deleteCachedDocument('functions', $function->getId());
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
|
@ -1513,7 +1498,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
|||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) {
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
|
@ -1530,6 +1516,14 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
|||
}
|
||||
|
||||
$dbForProject->deleteDocument('variables', $variable->getId());
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
|
||||
$dbForProject->deleteCachedDocument('functions', $function->getId());
|
||||
|
||||
$response->noContent();
|
||||
|
|
|
@ -208,8 +208,6 @@ class BuildsV1 extends Worker
|
|||
|
||||
Console::success("Build id: $buildId created");
|
||||
|
||||
$function->setAttribute('scheduleUpdatedAt', DateTime::now());
|
||||
|
||||
/** Set auto deploy */
|
||||
if ($deployment->getAttribute('activate') === true) {
|
||||
$function->setAttribute('deploymentInternalId', $deployment->getInternalId());
|
||||
|
@ -220,7 +218,7 @@ class BuildsV1 extends Worker
|
|||
/** Update function schedule */
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt'));
|
||||
$schedule->setAttribute('resourceUpdatedAt', DateTime::now());
|
||||
|
||||
$schedule
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php patch-create-missing-schedules $@
|
3
bin/patch-delete-schedule-updated-at-attribute
Normal file
3
bin/patch-delete-schedule-updated-at-attribute
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php patch-delete-schedule-updated-at-attribute $@
|
|
@ -44,10 +44,10 @@
|
|||
"appwrite/php-clamav": "1.1.*",
|
||||
"appwrite/php-runtimes": "0.11.*",
|
||||
"utopia-php/abuse": "0.18.*",
|
||||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.20.*",
|
||||
"utopia-php/analytics": "0.10.2",
|
||||
"utopia-php/cache": "0.8.*",
|
||||
"utopia-php/cli": "0.14.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.30.*",
|
||||
"utopia-php/queue": "0.5.*",
|
||||
|
@ -73,8 +73,7 @@
|
|||
"chillerlan/php-qrcode": "4.3.3",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"slickdeals/statsd": "3.1.0",
|
||||
"league/csv": "^9.0.0"
|
||||
"slickdeals/statsd": "3.1.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
|
|
432
composer.lock
generated
432
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d751d0dc8418f9789bfd99ade50cb9ca",
|
||||
"content-hash": "ca2a083ff1c0d0c77942674400137793",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -532,7 +532,207 @@
|
|||
"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"
|
||||
"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": "2022-08-28T14:55:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
|
||||
"reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.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
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
},
|
||||
"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.4.4"
|
||||
},
|
||||
"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-03-09T13:19:02+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": "laravel/pint",
|
||||
|
@ -600,93 +800,6 @@
|
|||
},
|
||||
"time": "2022-11-29T16:25:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
"version": "9.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/csv.git",
|
||||
"reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/b4418ede47fbd88facc34e40a16c8ce9153b961b",
|
||||
"reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": "^8.1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^2.1.2",
|
||||
"ext-dom": "*",
|
||||
"ext-xdebug": "*",
|
||||
"friendsofphp/php-cs-fixer": "^v3.14.3",
|
||||
"phpbench/phpbench": "^1.2.8",
|
||||
"phpstan/phpstan": "^1.10.4",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1.2",
|
||||
"phpstan/phpstan-phpunit": "^1.3.10",
|
||||
"phpstan/phpstan-strict-rules": "^1.5.0",
|
||||
"phpunit/phpunit": "^10.0.14"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Required to use the XMLConverter and 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": "https://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": "2023-03-11T15:57:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "matomo/device-detector",
|
||||
"version": "6.0.0",
|
||||
|
@ -1276,24 +1389,25 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
"version": "0.2.0",
|
||||
"version": "0.10.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/analytics.git",
|
||||
"reference": "adfc2d057a7f6ab618a77c8a20ed3e35485ff416"
|
||||
"reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/analytics/zipball/adfc2d057a7f6ab618a77c8a20ed3e35485ff416",
|
||||
"reference": "adfc2d057a7f6ab618a77c8a20ed3e35485ff416",
|
||||
"url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f",
|
||||
"reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4"
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "^0.15.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
"laravel/pint": "dev-main",
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -1305,16 +1419,6 @@
|
|||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Eldad Fux",
|
||||
"email": "eldad@appwrite.io"
|
||||
},
|
||||
{
|
||||
"name": "Torsten Dittmann",
|
||||
"email": "torsten@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "A simple library to track events & users.",
|
||||
"keywords": [
|
||||
"analytics",
|
||||
|
@ -1325,9 +1429,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/analytics/issues",
|
||||
"source": "https://github.com/utopia-php/analytics/tree/0.2.0"
|
||||
"source": "https://github.com/utopia-php/analytics/tree/0.10.2"
|
||||
},
|
||||
"time": "2021-03-23T21:33:07+00:00"
|
||||
"time": "2023-03-22T12:01:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/audit",
|
||||
|
@ -1429,16 +1533,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/cli",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/cli.git",
|
||||
"reference": "c30ef985a4e739758a0d95eb0706b357b6d8c086"
|
||||
"reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/c30ef985a4e739758a0d95eb0706b357b6d8c086",
|
||||
"reference": "c30ef985a4e739758a0d95eb0706b357b6d8c086",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea",
|
||||
"reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1446,8 +1550,10 @@
|
|||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"squizlabs/php_codesniffer": "^3.6"
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -1459,12 +1565,6 @@
|
|||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Eldad Fux",
|
||||
"email": "eldad@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "A simple CLI library to manage command line applications",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
@ -1476,9 +1576,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/cli/issues",
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.14.0"
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.15.0"
|
||||
},
|
||||
"time": "2022-10-09T10:19:07+00:00"
|
||||
"time": "2023-03-01T05:55:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/config",
|
||||
|
@ -1996,23 +2096,24 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/orchestration.git",
|
||||
"reference": "1d4f66684b8c4927f31b695817eae6d84aafd172"
|
||||
"reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/1d4f66684b8c4927f31b695817eae6d84aafd172",
|
||||
"reference": "1d4f66684b8c4927f31b695817eae6d84aafd172",
|
||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0",
|
||||
"reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "0.14.*"
|
||||
"utopia-php/cli": "0.15.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.2",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
},
|
||||
|
@ -2039,34 +2140,34 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/orchestration/issues",
|
||||
"source": "https://github.com/utopia-php/orchestration/tree/0.9.0"
|
||||
"source": "https://github.com/utopia-php/orchestration/tree/0.9.1"
|
||||
},
|
||||
"time": "2022-11-09T17:38:00+00:00"
|
||||
"time": "2023-03-17T15:05:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/platform",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/platform.git",
|
||||
"reference": "a53997f1882dc16ff67968aa5f436364c8c8043e"
|
||||
"reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a53997f1882dc16ff67968aa5f436364c8c8043e",
|
||||
"reference": "a53997f1882dc16ff67968aa5f436364c8c8043e",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a9e7a501f33e0da59779782359a747cb8d34cf6f",
|
||||
"reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "0.14.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/framework": "0.26.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"squizlabs/php_codesniffer": "^3.6"
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -2088,9 +2189,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/platform/issues",
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.3.2"
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.3.3"
|
||||
},
|
||||
"time": "2023-02-14T04:51:46+00:00"
|
||||
"time": "2023-03-07T08:52:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/pools",
|
||||
|
@ -2198,21 +2299,21 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/queue",
|
||||
"version": "0.5.1",
|
||||
"version": "0.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/queue.git",
|
||||
"reference": "4b39e1f232b2e619b0d7fb4004f2356df334ddc1"
|
||||
"reference": "310271c5cd477541208d7fa74a4dea64df8e04a0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/4b39e1f232b2e619b0d7fb4004f2356df334ddc1",
|
||||
"reference": "4b39e1f232b2e619b0d7fb4004f2356df334ddc1",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/310271c5cd477541208d7fa74a4dea64df8e04a0",
|
||||
"reference": "310271c5cd477541208d7fa74a4dea64df8e04a0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "0.14.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -2253,9 +2354,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/queue/issues",
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.5.1"
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.5.2"
|
||||
},
|
||||
"time": "2022-11-16T19:47:26+00:00"
|
||||
"time": "2023-03-07T08:54:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/registry",
|
||||
|
@ -2750,6 +2851,49 @@
|
|||
},
|
||||
"time": "2022-05-02T15:47:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/deprecations.git",
|
||||
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
|
||||
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.5",
|
||||
"psr/log": "^1|^2|^3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/deprecations/issues",
|
||||
"source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
|
||||
},
|
||||
"time": "2022-05-02T15:47:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.5.0",
|
||||
|
|
|
@ -134,7 +134,6 @@ services:
|
|||
- _APP_SMTP_SECURE
|
||||
- _APP_SMTP_USERNAME
|
||||
- _APP_SMTP_PASSWORD
|
||||
- _APP_HAMSTER_RECIPIENTS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_STORAGE_LIMIT
|
||||
- _APP_STORAGE_PREVIEW_LIMIT
|
||||
|
@ -573,8 +572,9 @@ services:
|
|||
- _APP_CONNECTIONS_DB_CONSOLE
|
||||
- _APP_CONNECTIONS_DB_PROJECT
|
||||
- _APP_CONNECTIONS_CACHE
|
||||
- _APP_HAMSTER_RECIPIENTS
|
||||
- _APP_HAMSTER_INTERVAL
|
||||
- _APP_HAMSTER_TIME
|
||||
- _APP_MIXPANEL_TOKEN
|
||||
|
||||
appwrite-maintenance:
|
||||
entrypoint: maintenance
|
||||
|
|
|
@ -13,6 +13,7 @@ use Appwrite\Platform\Tasks\SDKs;
|
|||
use Appwrite\Platform\Tasks\Specs;
|
||||
use Appwrite\Platform\Tasks\SSL;
|
||||
use Appwrite\Platform\Tasks\Hamster;
|
||||
use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute;
|
||||
use Appwrite\Platform\Tasks\Usage;
|
||||
use Appwrite\Platform\Tasks\Vars;
|
||||
use Appwrite\Platform\Tasks\Version;
|
||||
|
@ -32,6 +33,7 @@ class Tasks extends Service
|
|||
->addAction(Install::getName(), new Install())
|
||||
->addAction(Maintenance::getName(), new Maintenance())
|
||||
->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules())
|
||||
->addAction(PatchDeleteScheduleUpdatedAtAttribute::getName(), new PatchDeleteScheduleUpdatedAtAttribute())
|
||||
->addAction(Schedule::getName(), new Schedule())
|
||||
->addAction(Migrate::getName(), new Migrate())
|
||||
->addAction(SDKs::getName(), new SDKs())
|
||||
|
|
|
@ -10,53 +10,35 @@ use Utopia\CLI\Console;
|
|||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use League\Csv\Writer;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\Analytics\Adapter\Mixpanel;
|
||||
use Utopia\Analytics\Event;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class Hamster extends Action
|
||||
{
|
||||
private array $columns = [
|
||||
'Project ID',
|
||||
'Project Name',
|
||||
'Functions',
|
||||
'Deployments',
|
||||
'Members',
|
||||
'Domains',
|
||||
'Files',
|
||||
'Buckets',
|
||||
'Databases',
|
||||
'Documents',
|
||||
'Collections',
|
||||
'Storage',
|
||||
'Requests',
|
||||
'Bandwidth',
|
||||
'Users',
|
||||
'Sessions',
|
||||
'Executions'
|
||||
];
|
||||
|
||||
private array $usageStats = [
|
||||
'Files' => 'files.$all.count.total',
|
||||
'Buckets' => 'buckets.$all.count.total',
|
||||
'Databases' => 'databases.$all.count.total',
|
||||
'Documents' => 'documents.$all.count.total',
|
||||
'Collections' => 'collections.$all.count.total',
|
||||
'Storage' => 'project.$all.storage.size',
|
||||
'Requests' => 'project.$all.network.requests',
|
||||
'Bandwidth' => 'project.$all.network.bandwidth',
|
||||
'Users' => 'users.$all.count.total',
|
||||
'Sessions' => 'sessions.$all.requests.create',
|
||||
'Executions' => 'executions.$all.compute.total',
|
||||
private array $metrics = [
|
||||
'usage_files' => 'files.$all.count.total',
|
||||
'usage_buckets' => 'buckets.$all.count.total',
|
||||
'usage_databases' => 'databases.$all.count.total',
|
||||
'usage_documents' => 'documents.$all.count.total',
|
||||
'usage_collections' => 'collections.$all.count.total',
|
||||
'usage_storage' => 'project.$all.storage.size',
|
||||
'usage_requests' => 'project.$all.network.requests',
|
||||
'usage_bandwidth' => 'project.$all.network.bandwidth',
|
||||
'usage_users' => 'users.$all.count.total',
|
||||
'usage_sessions' => 'sessions.email.requests.create',
|
||||
'usage_executions' => 'executions.$all.compute.total',
|
||||
];
|
||||
|
||||
protected string $directory = '/usr/local';
|
||||
|
||||
protected string $path;
|
||||
|
||||
protected string $date;
|
||||
|
||||
protected Mixpanel $mixpanel;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'hamster';
|
||||
|
@ -64,128 +46,30 @@ class Hamster extends Action
|
|||
|
||||
public function __construct()
|
||||
{
|
||||
$this->mixpanel = new Mixpanel(App::getEnv('_APP_MIXPANEL_TOKEN', ''));
|
||||
|
||||
$this
|
||||
->desc('Get stats for projects')
|
||||
->inject('register')
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->callback(function (Registry $register, Group $pools, Cache $cache, Database $dbForConsole) {
|
||||
$this->action($register, $pools, $cache, $dbForConsole);
|
||||
->callback(function (Group $pools, Cache $cache, Database $dbForConsole) {
|
||||
$this->action($pools, $cache, $dbForConsole);
|
||||
});
|
||||
}
|
||||
|
||||
private function getStats(Database $dbForConsole, Database $dbForProject, Document $project): array
|
||||
private function getStatsPerProject(Group $pools, Cache $cache, Database $dbForConsole)
|
||||
{
|
||||
$stats = [];
|
||||
|
||||
/** Get Project ID */
|
||||
$stats['Project ID'] = $project->getId();
|
||||
|
||||
/** Get Project Name */
|
||||
$stats['Project Name'] = $project->getAttribute('name');
|
||||
|
||||
/** Get Total Functions */
|
||||
$stats['Functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Deployments */
|
||||
$stats['Deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Members */
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', null);
|
||||
if ($teamInternalId) {
|
||||
$stats['Members'] = $dbForConsole->count('memberships', [
|
||||
Query::equal('teamInternalId', [$teamInternalId])
|
||||
], APP_LIMIT_COUNT);
|
||||
} else {
|
||||
$stats['Members'] = 0;
|
||||
}
|
||||
|
||||
/** Get Domains */
|
||||
$stats['Domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Usage stats */
|
||||
$range = '90d';
|
||||
$periods = [
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
|
||||
$metrics = array_values($this->usageStats);
|
||||
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'),
|
||||
];
|
||||
}
|
||||
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
// Calculate aggregate of each metric
|
||||
$stats[$metric] = array_sum(array_column($stats[$metric], 'value'));
|
||||
}
|
||||
});
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
public function action(Registry $register, Group $pools, Cache $cache, Database $dbForConsole): void
|
||||
{
|
||||
|
||||
Console::title('Cloud Hamster V1');
|
||||
Console::success(APP_NAME . ' cloud hamster process v1 has started');
|
||||
|
||||
$interval = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default)
|
||||
|
||||
Console::loop(function () use ($register, $pools, $cache, $dbForConsole, $interval) {
|
||||
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Getting Cloud Usage Stats every {$interval} seconds");
|
||||
$loopStart = microtime(true);
|
||||
|
||||
/* Initialise new Utopia app */
|
||||
$app = new App('UTC');
|
||||
$console = $app->getResource('console');
|
||||
|
||||
/** CSV stuff */
|
||||
$this->date = date('Y-m-d');
|
||||
$this->path = "{$this->directory}/stats_{$this->date}.csv";
|
||||
$csv = Writer::createFromPath($this->path, 'w');
|
||||
$csv->insertOne($this->columns);
|
||||
|
||||
/** Database connections */
|
||||
$totalProjects = $dbForConsole->count('projects') + 1;
|
||||
Console::success("Found a total of: {$totalProjects} projects");
|
||||
|
||||
$projects = [$console];
|
||||
$count = 0;
|
||||
$limit = 30;
|
||||
$sum = 30;
|
||||
$offset = 0;
|
||||
while (!empty($projects)) {
|
||||
foreach ($projects as $project) {
|
||||
$this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools, $cache) {
|
||||
/**
|
||||
* Skip user projects with id 'console'
|
||||
*/
|
||||
if ($project->getId() === 'console') {
|
||||
continue;
|
||||
Console::info("Skipping project console");
|
||||
return;
|
||||
}
|
||||
|
||||
Console::info("Getting stats for {$project->getId()}");
|
||||
Console::log("Getting stats for {$project->getId()}");
|
||||
|
||||
try {
|
||||
$db = $project->getAttribute('database');
|
||||
|
@ -198,32 +82,199 @@ class Hamster extends Action
|
|||
$dbForProject->setDefaultDatabase('appwrite');
|
||||
$dbForProject->setNamespace('_' . $project->getInternalId());
|
||||
|
||||
$statsPerProject = $this->getStats($dbForConsole, $dbForProject, $project);
|
||||
$csv->insertOne(array_values($statsPerProject));
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage());
|
||||
$statsPerProject = [];
|
||||
|
||||
$statsPerProject['time'] = microtime(true);
|
||||
|
||||
/** Get Project ID */
|
||||
$statsPerProject['project_id'] = $project->getId();
|
||||
|
||||
/** Get project created time */
|
||||
$statsPerProject['project_created'] = $project->getAttribute('$createdAt');
|
||||
|
||||
/** Get Project Name */
|
||||
$statsPerProject['project_name'] = $project->getAttribute('name');
|
||||
|
||||
/** Get Total Functions */
|
||||
$statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Deployments */
|
||||
$statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Teams */
|
||||
$statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Total Members */
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', null);
|
||||
if ($teamInternalId) {
|
||||
$statsPerProject['custom_organization_members'] = $dbForConsole->count('memberships', [
|
||||
Query::equal('teamInternalId', [$teamInternalId])
|
||||
], APP_LIMIT_COUNT);
|
||||
} else {
|
||||
$statsPerProject['custom_organization_members'] = 0;
|
||||
}
|
||||
|
||||
/** Get Email and Name of the project owner */
|
||||
if ($teamInternalId) {
|
||||
$membership = $dbForConsole->findOne('memberships', [
|
||||
Query::equal('teamInternalId', [$teamInternalId]),
|
||||
]);
|
||||
|
||||
if (!$membership || $membership->isEmpty()) {
|
||||
throw new Exception('Membership not found. Skipping project : ' . $project->getId());
|
||||
}
|
||||
|
||||
$userInternalId = $membership->getAttribute('userInternalId', null);
|
||||
if ($userInternalId) {
|
||||
$user = $dbForConsole->findOne('users', [
|
||||
Query::equal('_id', [$userInternalId]),
|
||||
]);
|
||||
|
||||
$statsPerProject['email'] = $user->getAttribute('email', null);
|
||||
$statsPerProject['name'] = $user->getAttribute('name', null);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Domains */
|
||||
$statsPerProject['custom_domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT);
|
||||
|
||||
/** Get Platforms */
|
||||
$platforms = $dbForConsole->find('platforms', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_COUNT)
|
||||
]);
|
||||
|
||||
$statsPerProject['custom_platforms_web'] = sizeof(array_filter($platforms, function ($platform) {
|
||||
return $platform['type'] === 'web';
|
||||
}));
|
||||
|
||||
$statsPerProject['custom_platforms_android'] = sizeof(array_filter($platforms, function ($platform) {
|
||||
return $platform['type'] === 'android';
|
||||
}));
|
||||
|
||||
$statsPerProject['custom_platforms_iOS'] = sizeof(array_filter($platforms, function ($platform) {
|
||||
return str_contains($platform['type'], 'apple');
|
||||
}));
|
||||
|
||||
$statsPerProject['custom_platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) {
|
||||
return str_contains($platform['type'], 'flutter');
|
||||
}));
|
||||
|
||||
/** Get Usage $statsPerProject */
|
||||
$periods = [
|
||||
'infinity' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, &$statsPerProject) {
|
||||
foreach ($this->metrics as $key => $metric) {
|
||||
foreach ($periods as $periodKey => $periodValue) {
|
||||
$limit = $periodValue['limit'];
|
||||
$period = $periodValue['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$statsPerProject[$key . '_' . $periodKey] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$statsPerProject[$key . '_' . $periodKey][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
$statsPerProject[$key . '_' . $periodKey] = array_reverse($statsPerProject[$key . '_' . $periodKey]);
|
||||
// Calculate aggregate of each metric
|
||||
$statsPerProject[$key . '_' . $periodKey] = array_sum(array_column($statsPerProject[$key . '_' . $periodKey], 'value'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (isset($statsPerProject['email'])) {
|
||||
/** Send data to mixpanel */
|
||||
$res = $this->mixpanel->createProfile($statsPerProject['email'], '', [
|
||||
'name' => $statsPerProject['name'],
|
||||
'email' => $statsPerProject['email']
|
||||
]);
|
||||
|
||||
if (!$res) {
|
||||
Console::error('Failed to create user profile for project: ' . $project->getId());
|
||||
}
|
||||
|
||||
$event = new Event();
|
||||
$event
|
||||
->setName('Project Daily Usage')
|
||||
->setProps($statsPerProject);
|
||||
$res = $this->mixpanel->createEvent($event);
|
||||
if (!$res) {
|
||||
Console::error('Failed to create event for project: ' . $project->getId());
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error('Failed to send stats for project: ' . $project->getId());
|
||||
Console::error($e->getMessage());
|
||||
} finally {
|
||||
$pools
|
||||
->get($db)
|
||||
->reclaim();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$sum = \count($projects);
|
||||
public function action(Group $pools, Cache $cache, Database $dbForConsole): void
|
||||
{
|
||||
|
||||
$projects = $dbForConsole->find('projects', [
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
]);
|
||||
Console::title('Cloud Hamster V1');
|
||||
Console::success(APP_NAME . ' cloud hamster process has started');
|
||||
|
||||
$offset = $offset + $limit;
|
||||
$count = $count + $sum;
|
||||
$sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default)
|
||||
|
||||
Console::log('Iterated through ' . $count . '/' . $totalProjects . ' projects...');
|
||||
$jobInitTime = App::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes)
|
||||
$now = new \DateTime();
|
||||
$now->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
$next = new \DateTime($now->format("Y-m-d $jobInitTime"));
|
||||
$next->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
$delay = $next->getTimestamp() - $now->getTimestamp();
|
||||
|
||||
/**
|
||||
* If time passed for the target day.
|
||||
*/
|
||||
if ($delay <= 0) {
|
||||
$next->add(\DateInterval::createFromDateString('1 days'));
|
||||
$delay = $next->getTimestamp() - $now->getTimestamp();
|
||||
}
|
||||
|
||||
$this->sendEmail($register);
|
||||
Console::log('[' . $now->format("Y-m-d H:i:s.v") . '] Delaying for ' . $delay . ' setting loop to [' . $next->format("Y-m-d H:i:s.v") . ']');
|
||||
|
||||
Console::loop(function () use ($pools, $cache, $dbForConsole, $sleep) {
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Getting Cloud Usage Stats every {$sleep} seconds");
|
||||
$loopStart = microtime(true);
|
||||
|
||||
/* Initialise new Utopia app */
|
||||
$app = new App('UTC');
|
||||
|
||||
Console::info('Getting stats for all projects');
|
||||
$this->getStatsPerProject($pools, $cache, $dbForConsole);
|
||||
Console::success('Completed getting stats for all projects');
|
||||
|
||||
Console::info('Getting stats for all organizations');
|
||||
$this->getStatsPerOrganization($dbForConsole);
|
||||
Console::success('Completed getting stats for all organizations');
|
||||
|
||||
Console::info('Getting stats for all users');
|
||||
$this->getStatsPerUser($dbForConsole);
|
||||
Console::success('Completed getting stats for all users');
|
||||
|
||||
$pools
|
||||
->get('console')
|
||||
|
@ -232,40 +283,138 @@ class Hamster extends Action
|
|||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Cloud Stats took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
}, $sleep, $delay);
|
||||
}
|
||||
|
||||
private function sendEmail(Registry $register)
|
||||
protected function calculateByGroup(string $collection, Database $dbForConsole, callable $callback)
|
||||
{
|
||||
/** @var \PHPMailer\PHPMailer\PHPMailer $mail */
|
||||
$mail = $register->get('smtp');
|
||||
$count = 0;
|
||||
$chunk = 0;
|
||||
$limit = 50;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
|
||||
$mail->clearAddresses();
|
||||
$mail->clearAllRecipients();
|
||||
$mail->clearReplyTos();
|
||||
$mail->clearAttachments();
|
||||
$mail->clearBCCs();
|
||||
$mail->clearCCs();
|
||||
$executionStart = \microtime(true);
|
||||
|
||||
while ($sum === $limit) {
|
||||
$chunk++;
|
||||
|
||||
$results = $dbForConsole->find($collection, \array_merge([
|
||||
Query::limit($limit),
|
||||
Query::offset($count)
|
||||
]));
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
Console::log('Processing chunk #' . $chunk . '. Found ' . $sum . ' documents');
|
||||
|
||||
foreach ($results as $document) {
|
||||
call_user_func($callback, $dbForConsole, $document);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$executionEnd = \microtime(true);
|
||||
|
||||
Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
|
||||
}
|
||||
|
||||
protected function getStatsPerOrganization(Database $dbForConsole)
|
||||
{
|
||||
|
||||
$this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $document) {
|
||||
try {
|
||||
/** Addresses */
|
||||
$mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster');
|
||||
$recipients = explode(',', App::getEnv('_APP_HAMSTER_RECIPIENTS', ''));
|
||||
foreach ($recipients as $recipient) {
|
||||
$mail->addAddress($recipient);
|
||||
$statsPerOrganization = [];
|
||||
|
||||
/** Organization name */
|
||||
$statsPerOrganization['name'] = $document->getAttribute('name');
|
||||
|
||||
/** Get Email and of the organization owner */
|
||||
$membership = $dbForConsole->findOne('memberships', [
|
||||
Query::equal('teamInternalId', [$document->getInternalId()]),
|
||||
]);
|
||||
|
||||
if (!$membership || $membership->isEmpty()) {
|
||||
throw new Exception('Membership not found. Skipping organization : ' . $document->getId());
|
||||
}
|
||||
|
||||
/** Attachments */
|
||||
$mail->addAttachment($this->path);
|
||||
$userInternalId = $membership->getAttribute('userInternalId', null);
|
||||
if ($userInternalId) {
|
||||
$user = $dbForConsole->findOne('users', [
|
||||
Query::equal('_id', [$userInternalId]),
|
||||
]);
|
||||
|
||||
/** Content */
|
||||
$mail->Subject = "Cloud Report for {$this->date}";
|
||||
$mail->Body = "Please find the daily cloud report atttached";
|
||||
$statsPerOrganization['email'] = $user->getAttribute('email', null);
|
||||
}
|
||||
|
||||
$mail->send();
|
||||
Console::success('Email has been sent!');
|
||||
/** Organization Creation Date */
|
||||
$statsPerOrganization['created'] = $document->getAttribute('$createdAt');
|
||||
|
||||
/** Number of team members */
|
||||
$statsPerOrganization['members'] = $document->getAttribute('total');
|
||||
|
||||
/** Number of projects in this organization */
|
||||
$statsPerOrganization['projects'] = $dbForConsole->count('projects', [
|
||||
Query::equal('teamId', [$document->getId()]),
|
||||
Query::limit(APP_LIMIT_COUNT)
|
||||
]);
|
||||
|
||||
if (!isset($statsPerOrganization['email'])) {
|
||||
throw new Exception('Email not found. Skipping organization : ' . $document->getId());
|
||||
}
|
||||
|
||||
$event = new Event();
|
||||
$event
|
||||
->setName('Organization Daily Usage')
|
||||
->setProps($statsPerOrganization);
|
||||
$res = $this->mixpanel->createEvent($event);
|
||||
if (!$res) {
|
||||
throw new Exception('Failed to create event for organization : ' . $document->getId());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
|
||||
Console::error($e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function getStatsPerUser(Database $dbForConsole)
|
||||
{
|
||||
$this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $document) {
|
||||
try {
|
||||
$statsPerUser = [];
|
||||
|
||||
/** Organization name */
|
||||
$statsPerUser['name'] = $document->getAttribute('name');
|
||||
|
||||
/** Organization ID (needs to be stored as an email since mixpanel uses the email attribute as a distinctID) */
|
||||
$statsPerUser['email'] = $document->getAttribute('email');
|
||||
|
||||
/** Organization Creation Date */
|
||||
$statsPerUser['created'] = $document->getAttribute('$createdAt');
|
||||
|
||||
/** Number of teams this user is a part of */
|
||||
$statsPerUser['memberships'] = $dbForConsole->count('memberships', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_COUNT)
|
||||
]);
|
||||
|
||||
if (!isset($statsPerUser['email'])) {
|
||||
throw new Exception('User has no email: ' . $document->getId());
|
||||
}
|
||||
|
||||
/** Send data to mixpanel */
|
||||
$event = new Event();
|
||||
$event
|
||||
->setName('User Daily Usage')
|
||||
->setProps($statsPerUser);
|
||||
$res = $this->mixpanel->createEvent($event);
|
||||
|
||||
if (!$res) {
|
||||
throw new Exception('Failed to create user profile for user: ' . $document->getId());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error($e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Pools\Group;
|
||||
|
||||
class PatchDeleteScheduleUpdatedAtAttribute extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'patch-delete-schedule-updated-at-attribute';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Ensure function collections do not have scheduleUpdatedAt attribute')
|
||||
->inject('pools')
|
||||
->inject('dbForConsole')
|
||||
->inject('getProjectDB')
|
||||
->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB));
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over every function on every project to make sure there is a schedule. If not, recreate the schedule.
|
||||
*/
|
||||
public function action(Group $pools, Database $dbForConsole, callable $getProjectDB): void
|
||||
{
|
||||
Authorization::disable();
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
Console::title('PatchDeleteScheduleUpdatedAtAttribute V1');
|
||||
Console::success(APP_NAME . ' PatchDeleteScheduleUpdatedAtAttribute v1 has started');
|
||||
|
||||
$limit = 100;
|
||||
$projectCursor = null;
|
||||
while (true) {
|
||||
$projectsQueries = [Query::limit($limit)];
|
||||
if ($projectCursor !== null) {
|
||||
$projectsQueries[] = Query::cursorAfter($projectCursor);
|
||||
}
|
||||
$projects = $dbForConsole->find('projects', $projectsQueries);
|
||||
|
||||
if (count($projects) === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($projects as $project) {
|
||||
Console::log("Checking Project " . $project->getAttribute('name') . " (" . $project->getId() . ")");
|
||||
$dbForProject = $getProjectDB($project);
|
||||
|
||||
try {
|
||||
/**
|
||||
* Delete 'scheduleUpdatedAt' attribute
|
||||
*/
|
||||
$dbForProject->deleteAttribute('functions', 'scheduleUpdatedAt');
|
||||
$dbForProject->deleteCachedCollection('functions');
|
||||
Console::success("'scheduleUpdatedAt' deleted.");
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'scheduleUpdatedAt' errored: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
$pools->reclaim();
|
||||
}
|
||||
|
||||
$projectCursor = $projects[array_key_last($projects)];
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue