mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
499eb0c3de | ||
|
|
f528656d61 | ||
|
|
051a31f417 | ||
|
|
4fb09b7671 | ||
|
|
514a5c37fd | ||
|
|
078c3dd46f | ||
|
|
a1369ff92a | ||
|
|
eb039d5ff5 | ||
|
|
b713474036 | ||
|
|
7471ffb355 | ||
|
|
3b1d4fd6e2 | ||
|
|
44b225183e | ||
|
|
406aba557a | ||
|
|
cfaff942bd | ||
|
|
0ba087dbe2 | ||
|
|
795004f174 | ||
|
|
2ace4babfc | ||
|
|
e35c6c3c79 | ||
|
|
607dca0b08 | ||
|
|
fa3d5f8267 | ||
|
|
c32133073c | ||
|
|
68efce9a4b | ||
|
|
67af8eea41 | ||
|
|
c7604e9520 | ||
|
|
8dbdd8a2fe | ||
|
|
6a377a4ef1 | ||
|
|
553780153e | ||
|
|
7a9b43b89a | ||
|
|
a0c08bfd52 | ||
|
|
25e7767571 | ||
|
|
bdecdbd890 | ||
|
|
9c4fcdabce | ||
|
|
7922b092d3 | ||
|
|
cd82bed661 | ||
|
|
a170e182d0 | ||
|
|
366e71948b | ||
|
|
2ee97ff50e | ||
|
|
208d3dd523 | ||
|
|
fe703e3137 | ||
|
|
f34b8a6865 | ||
|
|
8c23743ae5 | ||
|
|
7088556ff9 | ||
|
|
8a102d52e7 | ||
|
|
43ba874ccf | ||
|
|
9a7b7447a7 | ||
|
|
c8eea7dd11 | ||
|
|
c1b0ef206b | ||
|
|
c58f5a4522 | ||
|
|
9d0dca5919 | ||
|
|
bf3504fc53 | ||
|
|
d58ae07c03 | ||
|
|
549d88c105 | ||
|
|
59e54d44c2 | ||
|
|
9413e8a1bf | ||
|
|
1ecd139a4d | ||
|
|
ca46e27e8a |
@@ -50,10 +50,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=
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = 3.2.0
|
||||
branch = feat-usage-1.4
|
||||
|
||||
+35
@@ -213,6 +213,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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+2
-2
@@ -74,7 +74,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 && \
|
||||
@@ -94,7 +93,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
|
||||
chmod +x /usr/local/bin/worker-migrations && \
|
||||
chmod +x /usr/local/bin/worker-usage
|
||||
|
||||
# Cloud Executabless
|
||||
RUN chmod +x /usr/local/bin/hamster && \
|
||||
|
||||
-23
@@ -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());
|
||||
|
||||
@@ -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,14 @@ $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],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
];
|
||||
|
||||
$projectCollections = array_merge([
|
||||
'databases' => [
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -335,54 +335,6 @@ return [
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'category' => 'InfluxDB',
|
||||
'description' => 'Appwrite uses an InfluxDB server for managing time-series data and server stats. The InfluxDB env vars are used to allow Appwrite server to connect to the InfluxDB container.',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_INFLUXDB_HOST',
|
||||
'description' => 'InfluxDB server host name address. Default value is: \'influxdb\'.',
|
||||
'introduction' => '',
|
||||
'default' => 'influxdb',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_INFLUXDB_PORT',
|
||||
'description' => 'InfluxDB server TCP port. Default value is: \'8086\'.',
|
||||
'introduction' => '',
|
||||
'default' => '8086',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'category' => 'StatsD',
|
||||
'description' => 'Appwrite uses a StatsD server for aggregating and sending stats data over a fast UDP connection. The StatsD env vars are used to allow Appwrite server to connect to the StatsD container.',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_STATSD_HOST',
|
||||
'description' => 'StatsD server host name address. Default value is: \'telegraf\'.',
|
||||
'introduction' => '',
|
||||
'default' => 'telegraf',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STATSD_PORT',
|
||||
'description' => 'StatsD server TCP port. Default value is: \'8125\'.',
|
||||
'introduction' => '',
|
||||
'default' => '8125',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'category' => 'SMTP',
|
||||
'description' => "Appwrite is using an SMTP server for emailing your projects users and server admins. The SMTP env vars are used to allow Appwrite server to connect to the SMTP container.\n\nIf running in production, it might be easier to use a 3rd party SMTP server as it might be a little more difficult to set up a production SMTP server that will not send all your emails into your user\'s SPAM folder.",
|
||||
|
||||
+1
-1
Submodule app/console updated: c57a776730...9b4bcb8140
@@ -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')
|
||||
@@ -173,8 +172,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')
|
||||
@@ -427,8 +424,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)
|
||||
@@ -1097,8 +1092,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')
|
||||
@@ -1472,8 +1465,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')
|
||||
@@ -1651,7 +1642,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')
|
||||
@@ -1672,7 +1662,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')
|
||||
@@ -1695,7 +1684,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')
|
||||
@@ -1734,7 +1722,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')
|
||||
@@ -1795,7 +1782,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')
|
||||
@@ -1843,7 +1829,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')
|
||||
@@ -1878,7 +1863,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')
|
||||
@@ -1944,7 +1928,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')
|
||||
@@ -2014,7 +1997,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')
|
||||
@@ -2073,7 +2055,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')
|
||||
@@ -2107,7 +2088,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')
|
||||
@@ -2151,7 +2131,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')
|
||||
@@ -2228,7 +2207,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')
|
||||
@@ -2313,7 +2291,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')
|
||||
@@ -2375,7 +2352,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')
|
||||
@@ -2558,7 +2534,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')
|
||||
@@ -2645,7 +2620,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')
|
||||
@@ -2806,7 +2780,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')
|
||||
@@ -2867,7 +2840,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')
|
||||
@@ -2963,7 +2935,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')
|
||||
|
||||
+146
-346
@@ -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);
|
||||
});
|
||||
|
||||
+123
-174
@@ -13,7 +13,6 @@ use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Appwrite\Usage\Stats;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -454,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')
|
||||
@@ -557,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 +1454,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));
|
||||
|
||||
@@ -1726,6 +1674,15 @@ 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, 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($executionResponse['duration'] * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($executionResponse['duration'] * 1000)) // per function
|
||||
;
|
||||
} catch (\Throwable $th) {
|
||||
$durationEnd = \microtime(true);
|
||||
|
||||
@@ -1742,14 +1699,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);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
@@ -14,11 +15,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 +30,76 @@ App::get('/v1/project/usage')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'project.$all.network.requests',
|
||||
'project.$all.network.bandwidth',
|
||||
'project.$all.storage.size',
|
||||
'users.$all.count.total',
|
||||
'databases.$all.count.total',
|
||||
'documents.$all.count.total',
|
||||
'executions.$all.compute.total',
|
||||
'buckets.$all.count.total'
|
||||
];
|
||||
$periods = Config::getParam('usage', []);
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
METRIC_NETWORK_REQUESTS,
|
||||
METRIC_NETWORK_INBOUND,
|
||||
METRIC_NETWORK_OUTBOUND,
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_DOCUMENTS,
|
||||
METRIC_DATABASES,
|
||||
METRIC_USERS,
|
||||
METRIC_BUCKETS,
|
||||
METRIC_FILES_STORAGE
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $days['limit'];
|
||||
$period = $days['period'];
|
||||
$results = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
$stats[$metric] = [];
|
||||
foreach ($results as $result) {
|
||||
$stats[$metric][$result->getAttribute('time')] = [
|
||||
'value' => $result->getAttribute('value'),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'requests' => $stats[$metrics[0]] ?? [],
|
||||
'network' => $stats[$metrics[1]] ?? [],
|
||||
'storage' => $stats[$metrics[2]] ?? [],
|
||||
'users' => $stats[$metrics[3]] ?? [],
|
||||
'databases' => $stats[$metrics[4]] ?? [],
|
||||
'documents' => $stats[$metrics[5]] ?? [],
|
||||
'executions' => $stats[$metrics[6]] ?? [],
|
||||
'buckets' => $stats[$metrics[7]] ?? [],
|
||||
]);
|
||||
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
$usage[$metric] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
$leap += $days['factor'];
|
||||
$formatDate = date($format, $leap);
|
||||
$usage[$metric][] = [
|
||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||
'date' => $formatDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_PROJECT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'requestsTotal' => ($usage[$metrics[0]]),
|
||||
'network' => ($usage[$metrics[1]] + $usage[$metrics[2]]),
|
||||
'executionsTotal' => $usage[$metrics[3]],
|
||||
'documentsTotal' => $usage[$metrics[4]],
|
||||
'databasesTotal' => $usage[$metrics[5]],
|
||||
'usersTotal' => $usage[$metrics[6]],
|
||||
'bucketsTotal' => $usage[$metrics[7]],
|
||||
'filesStorage' => $usage[$metrics[8]],
|
||||
]), Response::MODEL_USAGE_PROJECT);
|
||||
});
|
||||
|
||||
|
||||
// Variables
|
||||
|
||||
App::post('/v1/project/variables')
|
||||
|
||||
+94
-188
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Hash\Sha;
|
||||
use Appwrite\ClamAV\Network;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -52,7 +51,6 @@ App::post('/v1/storage/buckets')
|
||||
->label('event', 'buckets.[bucketId].create')
|
||||
->label('audits.event', 'bucket.create')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('usage.metric', 'buckets.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'createBucket')
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
+46
-111
@@ -114,7 +114,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')
|
||||
@@ -147,7 +146,6 @@ App::post('/v1/users/bcrypt')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createBcryptUser')
|
||||
@@ -178,7 +176,6 @@ App::post('/v1/users/md5')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createMD5User')
|
||||
@@ -209,7 +206,6 @@ App::post('/v1/users/argon2')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createArgon2User')
|
||||
@@ -240,7 +236,6 @@ App::post('/v1/users/sha')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createSHAUser')
|
||||
@@ -278,7 +273,6 @@ App::post('/v1/users/phpass')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createPHPassUser')
|
||||
@@ -309,7 +303,6 @@ App::post('/v1/users/scrypt')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createScryptUser')
|
||||
@@ -353,7 +346,6 @@ App::post('/v1/users/scrypt-modified')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.create')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createScryptModifiedUser')
|
||||
@@ -384,7 +376,6 @@ App::get('/v1/users')
|
||||
->desc('List users')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'list')
|
||||
@@ -433,7 +424,6 @@ App::get('/v1/users/:userId')
|
||||
->desc('Get user')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'get')
|
||||
@@ -459,7 +449,6 @@ App::get('/v1/users/:userId/prefs')
|
||||
->desc('Get user preferences')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'getPrefs')
|
||||
@@ -487,7 +476,6 @@ App::get('/v1/users/:userId/sessions')
|
||||
->desc('List user sessions')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'listSessions')
|
||||
@@ -529,7 +517,6 @@ App::get('/v1/users/:userId/memberships')
|
||||
->desc('List user memberships')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'listMemberships')
|
||||
@@ -569,7 +556,6 @@ App::get('/v1/users/:userId/logs')
|
||||
->desc('List user logs')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'listLogs')
|
||||
@@ -651,7 +637,6 @@ App::get('/v1/users/identities')
|
||||
->desc('List Identities')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'listIdentities')
|
||||
@@ -704,7 +689,6 @@ App::patch('/v1/users/:userId/status')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateStatus')
|
||||
@@ -740,7 +724,6 @@ App::put('/v1/users/:userId/labels')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateLabels')
|
||||
@@ -778,7 +761,6 @@ App::patch('/v1/users/:userId/verification/phone')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'verification.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updatePhoneVerification')
|
||||
@@ -815,7 +797,6 @@ App::patch('/v1/users/:userId/name')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateName')
|
||||
@@ -853,7 +834,6 @@ App::patch('/v1/users/:userId/password')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updatePassword')
|
||||
@@ -918,7 +898,6 @@ App::patch('/v1/users/:userId/email')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateEmail')
|
||||
@@ -974,7 +953,6 @@ App::patch('/v1/users/:userId/phone')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.update')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updatePhone')
|
||||
@@ -1019,7 +997,6 @@ App::patch('/v1/users/:userId/verification')
|
||||
->label('audits.event', 'verification.update')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateEmailVerification')
|
||||
@@ -1052,7 +1029,6 @@ App::patch('/v1/users/:userId/prefs')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.prefs')
|
||||
->label('scope', 'users.write')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updatePrefs')
|
||||
@@ -1088,7 +1064,6 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'session.delete')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'deleteSession')
|
||||
@@ -1132,7 +1107,6 @@ App::delete('/v1/users/:userId/sessions')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'session.delete')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'deleteSessions')
|
||||
@@ -1175,7 +1149,6 @@ App::delete('/v1/users/:userId')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'user.delete')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'delete')
|
||||
@@ -1218,7 +1191,6 @@ App::delete('/v1/users/identities/:identityId')
|
||||
->label('scope', 'users.write')
|
||||
->label('audits.event', 'identity.delete')
|
||||
->label('audits.resource', 'identity/{request.$identityId}')
|
||||
->label('usage.metric', 'users.{scope}.requests.delete')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'deleteIdentity')
|
||||
@@ -1254,96 +1226,59 @@ App::get('/v1/users/usage')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
|
||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('register')
|
||||
->action(function (string $range, string $provider, Response $response, Database $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_USERS,
|
||||
METRIC_SESSIONS,
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'users.$all.count.total',
|
||||
'users.$all.requests.create',
|
||||
'users.$all.requests.read',
|
||||
'users.$all.requests.update',
|
||||
'users.$all.requests.delete',
|
||||
'sessions.$all.requests.create',
|
||||
'sessions.$all.requests.delete',
|
||||
"sessions.$provider.requests.create",
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
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,
|
||||
'usersCount' => $stats['users.$all.count.total'] ?? [],
|
||||
'usersCreate' => $stats['users.$all.requests.create'] ?? [],
|
||||
'usersRead' => $stats['users.$all.requests.read'] ?? [],
|
||||
'usersUpdate' => $stats['users.$all.requests.update'] ?? [],
|
||||
'usersDelete' => $stats['users.$all.requests.delete'] ?? [],
|
||||
'sessionsCreate' => $stats['sessions.$all.requests.create'] ?? [],
|
||||
'sessionsProviderCreate' => $stats["sessions.$provider.requests.create"] ?? [],
|
||||
'sessionsDelete' => $stats['sessions.$all.requests.delete' ?? []]
|
||||
]);
|
||||
$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_USERS);
|
||||
$response->dynamic(new Document([
|
||||
'range' => $range,
|
||||
'usersTotal' => $usage[$metrics[0]],
|
||||
'sessionsTotal' => $usage[$metrics[1]],
|
||||
]), Response::MODEL_USAGE_USERS);
|
||||
});
|
||||
|
||||
+126
-69
@@ -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,93 @@ $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;
|
||||
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 +152,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 +237,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);
|
||||
|
||||
@@ -362,14 +408,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();
|
||||
|
||||
@@ -522,35 +568,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);
|
||||
}
|
||||
});
|
||||
|
||||
+4
-29
@@ -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('runtimes-v2', __DIR__ . '/config/runtimes-v2.php');
|
||||
Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes
|
||||
@@ -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'),
|
||||
|
||||
@@ -71,7 +71,6 @@ services:
|
||||
- mariadb
|
||||
- redis
|
||||
# - clamav
|
||||
- influxdb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
@@ -106,8 +105,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
|
||||
@@ -144,8 +141,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
|
||||
@@ -594,38 +589,6 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
appwrite-usage:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
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_USAGE_STATS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-schedule:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
@@ -751,26 +714,41 @@ services:
|
||||
# volumes:
|
||||
# - appwrite-uploads:/storage/uploads
|
||||
|
||||
influxdb:
|
||||
image: appwrite/influxdb:1.5.0
|
||||
container_name: appwrite-influxdb
|
||||
appwrite-worker-usage:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
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:
|
||||
@@ -788,5 +766,4 @@ volumes:
|
||||
appwrite-certificates:
|
||||
appwrite-functions:
|
||||
appwrite-builds:
|
||||
appwrite-influxdb:
|
||||
appwrite-config:
|
||||
|
||||
+10
-5
@@ -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']);
|
||||
|
||||
+12
-16
@@ -501,23 +501,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
|
||||
|
||||
+20
-22
@@ -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');
|
||||
@@ -128,6 +129,8 @@ Server::setResource('execute', function () {
|
||||
if ($execution->isEmpty()) {
|
||||
throw new Exception('Failed to create or read execution');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($execution->getAttribute('status') !== 'processing') {
|
||||
@@ -277,20 +280,15 @@ 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, 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))
|
||||
->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);
|
||||
@@ -302,10 +300,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)) {
|
||||
@@ -348,7 +346,7 @@ $server->job()
|
||||
Console::success('Iterating function: ' . $function->getAttribute('name'));
|
||||
$execute(
|
||||
log: $log,
|
||||
statsd: $statsd,
|
||||
queueForUsage: $queueForUsage,
|
||||
dbForProject: $dbForProject,
|
||||
project: $project,
|
||||
function: $function,
|
||||
@@ -397,7 +395,7 @@ $server->job()
|
||||
path: $payload['path'] ?? '',
|
||||
method: $payload['method'] ?? 'POST',
|
||||
headers: $payload['headers'] ?? [],
|
||||
statsd: $statsd,
|
||||
queueForUsage: $queueForUsage,
|
||||
);
|
||||
break;
|
||||
case 'schedule':
|
||||
@@ -417,7 +415,7 @@ $server->job()
|
||||
path: $payload['path'] ?? '/',
|
||||
method: $payload['method'] ?? 'POST',
|
||||
headers: $payload['headers'] ?? [],
|
||||
statsd: $statsd,
|
||||
queueForUsage: $queueForUsage,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../worker.php';
|
||||
|
||||
use Swoole\Timer;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Queue\Server;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
Authorization::disable();
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
$stats = [];
|
||||
|
||||
$periods['1h'] = 'Y-m-d H:00';
|
||||
$periods['1d'] = 'Y-m-d 00:00';
|
||||
//$periods['1m'] = 'Y-m-1 00:00';
|
||||
$periods['inf'] = '0000-00-00 00:00';
|
||||
|
||||
const INFINITY_PERIOD = '_inf_';
|
||||
|
||||
/**
|
||||
* On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files.
|
||||
* When we remove a parent document we need to deduct his children aggregation from the project scope.
|
||||
*/
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
QUEUE=v1-usage php /usr/src/code/app/workers/usage.php $@
|
||||
@@ -71,12 +71,10 @@
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.2",
|
||||
"influxdb/influxdb-php": "1.15.2",
|
||||
"phpmailer/phpmailer": "6.8.0",
|
||||
"chillerlan/php-qrcode": "4.3.4",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"slickdeals/statsd": "3.1.0",
|
||||
"league/csv": "9.7.1"
|
||||
},
|
||||
"repositories": [
|
||||
|
||||
Generated
+7
-726
@@ -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": "13a3bdc7c1dec5756bf58ec73a49753d",
|
||||
"content-hash": "8c487168571ef67391005a55d93ad801",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -447,398 +447,6 @@
|
||||
],
|
||||
"time": "2022-09-10T18:51:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9",
|
||||
"reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.5.3 || ^2.0.1",
|
||||
"guzzlehttp/psr7": "^1.9.1 || ^2.5.1",
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-client-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"ext-curl": "*",
|
||||
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
|
||||
"php-http/message-factory": "^1.1",
|
||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Required for CURL handler support",
|
||||
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Jeremy Lindblom",
|
||||
"email": "jeremeamia@gmail.com",
|
||||
"homepage": "https://github.com/jeremeamia"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"psr-18",
|
||||
"psr-7",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-27T10:20:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d",
|
||||
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-03T15:11:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
|
||||
"reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"ralouphie/getallheaders": "^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"http-interop/http-factory-tests": "^0.9",
|
||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://sagikazarmark.hu"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.6.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-27T10:13:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "influxdb/influxdb-php",
|
||||
"version": "1.15.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/influxdata/influxdb-php.git",
|
||||
"reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03",
|
||||
"reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"php": "^5.5 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dms/phpunit-arraysubset-asserts": "^0.2.1",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Curl extension, needed for Curl driver",
|
||||
"stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"InfluxDB\\": "src/InfluxDB"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Stephen Hoogendijk",
|
||||
"email": "stephen@tca0.nl"
|
||||
},
|
||||
{
|
||||
"name": "Daniel Martinez",
|
||||
"email": "danimartcas@hotmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Gianluca Arbezzano",
|
||||
"email": "gianarb92@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "InfluxDB client library for PHP",
|
||||
"keywords": [
|
||||
"client",
|
||||
"influxdata",
|
||||
"influxdb",
|
||||
"influxdb class",
|
||||
"influxdb client",
|
||||
"influxdb library",
|
||||
"time series"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/influxdata/influxdb-php/issues",
|
||||
"source": "https://github.com/influxdata/influxdb-php/tree/1.15.2"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2020-12-26T17:45:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.0.5",
|
||||
@@ -1050,16 +658,16 @@
|
||||
},
|
||||
{
|
||||
"name": "matomo/device-detector",
|
||||
"version": "6.1.5",
|
||||
"version": "6.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matomo-org/device-detector.git",
|
||||
"reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4"
|
||||
"reference": "5cbea85106e561c7138d03603eb6e05128480409"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/40ca2990dba2c1719e5c62168e822e0b86c167d4",
|
||||
"reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4",
|
||||
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409",
|
||||
"reference": "5cbea85106e561c7138d03603eb6e05128480409",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1115,7 +723,7 @@
|
||||
"source": "https://github.com/matomo-org/matomo",
|
||||
"wiki": "https://dev.matomo.org/"
|
||||
},
|
||||
"time": "2023-08-17T16:17:41+00:00"
|
||||
"time": "2023-10-02T10:01:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mongodb/mongodb",
|
||||
@@ -1316,166 +924,6 @@
|
||||
],
|
||||
"time": "2023-03-06T14:43:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
"homepage": "https://github.com/php-fig/http-client",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-client",
|
||||
"psr",
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"time": "2023-09-23T14:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "e616d01114759c4c489f93b099585439f795fe35"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
|
||||
"reference": "e616d01114759c4c489f93b099585439f795fe35",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory/tree/1.0.2"
|
||||
},
|
||||
"time": "2023-04-10T20:10:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.4",
|
||||
@@ -1526,50 +974,6 @@
|
||||
},
|
||||
"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",
|
||||
@@ -1653,129 +1057,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",
|
||||
@@ -6019,5 +5300,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
||||
+10
-54
@@ -127,8 +127,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
|
||||
@@ -165,8 +163,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
|
||||
@@ -647,36 +643,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
|
||||
@@ -841,26 +839,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
|
||||
@@ -924,26 +902,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:
|
||||
@@ -981,6 +939,4 @@ volumes:
|
||||
appwrite-certificates:
|
||||
appwrite-functions:
|
||||
appwrite-builds:
|
||||
appwrite-influxdb:
|
||||
appwrite-config:
|
||||
# appwrite-chronograf:
|
||||
|
||||
@@ -68,7 +68,7 @@ abstract class Migration
|
||||
'1.4.1' => 'V19',
|
||||
'1.4.2' => 'V19',
|
||||
'1.4.3' => 'V19',
|
||||
'1.4.4' => 'V19',
|
||||
'1.4.4' => 'V20',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,6 @@ abstract class Migration
|
||||
$sum = 0;
|
||||
$nextDocument = null;
|
||||
$collectionCount = $this->projectDB->count($collectionId);
|
||||
|
||||
do {
|
||||
$queries = [Query::limit($this->limit)];
|
||||
if ($nextDocument !== null) {
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Migration\Version;
|
||||
|
||||
use Appwrite\Migration\Migration;
|
||||
use PDOException;
|
||||
use Throwable;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception;
|
||||
use Utopia\Database\Exception\Authorization;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Structure;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
class V20 extends Migration
|
||||
{
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
if ($this->project->getInternalId() == 'console') {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SubQueries for Performance.
|
||||
*/
|
||||
foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables'] as $name) {
|
||||
Database::addFilter(
|
||||
$name,
|
||||
fn () => null,
|
||||
fn () => []
|
||||
);
|
||||
}
|
||||
|
||||
$this->migrateUsageMetrics('project.$all.network.requests', 'network.requests');
|
||||
$this->migrateUsageMetrics('project.$all.network.outbound', 'network.outbound');
|
||||
$this->migrateUsageMetrics('project.$all.network.inbound', 'network.inbound');
|
||||
$this->migrateUsageMetrics('users.$all.count.total', 'users');
|
||||
$this->migrateSessionsMetric();
|
||||
|
||||
Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
|
||||
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
|
||||
|
||||
Console::info('Migrating Functions');
|
||||
$this->migrateFunctions();
|
||||
|
||||
Console::info('Migrating Databases');
|
||||
$this->migrateDatabases();
|
||||
|
||||
Console::info('Migrating Collections');
|
||||
$this->migrateCollections();
|
||||
|
||||
Console::info('Migrating Buckets');
|
||||
$this->migrateBuckets();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Authorization
|
||||
* @throws Exception
|
||||
* @throws Structure
|
||||
*/
|
||||
protected function migrateSessionsMetric(): void
|
||||
{
|
||||
/**
|
||||
* Creating inf metric
|
||||
*/
|
||||
|
||||
Console::info('Migrating Sessions metric');
|
||||
|
||||
$sessionsCreated = $this->projectDB->sum('stats', 'value', [
|
||||
Query::equal('metric', [
|
||||
'sessions.email-password.requests.create',
|
||||
'sessions.magic-url.requests.create',
|
||||
'sessions.anonymous.requests.create',
|
||||
'sessions.invites.requests.create',
|
||||
'sessions.jwt.requests.create',
|
||||
'sessions.phone.requests.create'
|
||||
]),
|
||||
Query::equal('period', ['1d']),
|
||||
]);
|
||||
|
||||
$query = $this->projectDB->findOne('stats', [
|
||||
Query::equal('metric', ['sessions.$all.requests.delete']),
|
||||
Query::equal('period', ['1d']),
|
||||
]);
|
||||
|
||||
$sessionsDeleted = $query['value'] ?? 0;
|
||||
$value = $sessionsCreated - $sessionsDeleted;
|
||||
$this->createInfMetric('sessions', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $metric
|
||||
* @param int $value
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws Authorization
|
||||
* @throws Structure
|
||||
*/
|
||||
protected function createInfMetric(string $metric, int $value): void
|
||||
{
|
||||
|
||||
try {
|
||||
/**
|
||||
* Creating inf metric
|
||||
*/
|
||||
console::log("Creating inf metric to {$metric}");
|
||||
$id = \md5("_inf_{$metric}");
|
||||
$this->projectDB->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'metric' => $metric,
|
||||
'period' => 'inf',
|
||||
'value' => $value,
|
||||
'time' => null,
|
||||
'region' => 'default',
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
console::log("Error while creating inf metric: duplicate id {$metric} {$id}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function migrateUsageMetrics(string $from, string $to): void
|
||||
{
|
||||
/**
|
||||
* inf metric
|
||||
*/
|
||||
if (
|
||||
str_contains($from, '$all') ||
|
||||
str_contains($from, '.total')
|
||||
) {
|
||||
$query = $this->projectDB->sum('stats', 'value', [
|
||||
Query::equal('metric', [$from]),
|
||||
Query::equal('period', ['1d']),
|
||||
]);
|
||||
|
||||
$value = $query ?? 0;
|
||||
$this->createInfMetric($to, $value);
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Update old metric format to new
|
||||
*/
|
||||
$limit = 1000;
|
||||
$sum = $limit;
|
||||
$total = 0;
|
||||
$latestDocument = null;
|
||||
while ($sum === $limit) {
|
||||
$paginationQueries = [Query::limit($limit)];
|
||||
if ($latestDocument !== null) {
|
||||
$paginationQueries[] = Query::cursorAfter($latestDocument);
|
||||
}
|
||||
$stats = $this->projectDB->find('stats', \array_merge($paginationQueries, [
|
||||
Query::equal('metric', [$from]),
|
||||
]));
|
||||
|
||||
$sum = count($stats);
|
||||
$total = $total + $sum;
|
||||
foreach ($stats as $stat) {
|
||||
$format = $stat['period'] === '1d' ? 'Y-m-d 00:00' : 'Y-m-d H:00';
|
||||
$time = date($format, strtotime($stat['time']));
|
||||
$this->projectDB->deleteDocument('stats', $stat->getId());
|
||||
$stat->setAttribute('$id', \md5("{$time}_{$stat['period']}_{$to}"));
|
||||
$stat->setAttribute('metric', $to);
|
||||
$this->projectDB->createDocument('stats', $stat);
|
||||
console::log("deleting metric {$from} and creating {$to}");
|
||||
}
|
||||
$latestDocument = !empty(array_key_last($stats)) ? $stats[array_key_last($stats)] : null;
|
||||
}
|
||||
} catch (Throwable $th) {
|
||||
Console::warning("Error while updating metric {$from} " . $th->getMessage());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Migrate functions.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function migrateFunctions(): void
|
||||
{
|
||||
|
||||
$this->migrateUsageMetrics('deployment.$all.storage.size', 'deployments.storage');
|
||||
$this->migrateUsageMetrics('builds.$all.compute.total', 'builds');
|
||||
$this->migrateUsageMetrics('builds.$all.compute.time', 'builds.compute');
|
||||
$this->migrateUsageMetrics('executions.$all.compute.total', 'executions');
|
||||
$this->migrateUsageMetrics('executions.$all.compute.time', 'executions.compute');
|
||||
|
||||
foreach ($this->documentsIterator('functions') as $function) {
|
||||
Console::log("Migrating Functions usage stats of {$function->getId()} ({$function->getAttribute('name')})");
|
||||
|
||||
$functionId = $function->getId();
|
||||
$functionInternalId = $function->getInternalId();
|
||||
|
||||
$this->migrateUsageMetrics("deployment.$functionId.storage.size", "function.$functionInternalId.deployments.storage");
|
||||
$this->migrateUsageMetrics("builds.$functionId.compute.total", "$functionInternalId.builds");
|
||||
$this->migrateUsageMetrics("builds.$functionId.compute.time", "$functionInternalId.builds.compute");
|
||||
$this->migrateUsageMetrics("executions.$functionId.compute.total", "$functionInternalId.executions");
|
||||
$this->migrateUsageMetrics("executions.$functionId.compute.time", "$functionInternalId.executions.compute");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate Databases.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function migrateDatabases(): void
|
||||
{
|
||||
// Project level
|
||||
$this->migrateUsageMetrics('databases.$all.count.total', 'databases');
|
||||
$this->migrateUsageMetrics('collections.$all.count.total', 'collections');
|
||||
$this->migrateUsageMetrics('documents.$all.count.total', 'documents');
|
||||
|
||||
foreach ($this->documentsIterator('databases') as $database) {
|
||||
Console::log("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})");
|
||||
|
||||
$databaseTable = "database_{$database->getInternalId()}";
|
||||
|
||||
// Database level
|
||||
$databaseId = $database->getId();
|
||||
$databaseInternalId = $database->getInternalId();
|
||||
|
||||
$this->migrateUsageMetrics("collections.$databaseId.count.total", "$databaseInternalId.collections");
|
||||
$this->migrateUsageMetrics("documents.$databaseId.count.total", "$databaseInternalId.documents");
|
||||
|
||||
foreach ($this->documentsIterator($databaseTable) as $collection) {
|
||||
$collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}";
|
||||
Console::log("Migrating Collections of {$collectionTable} {$collection->getId()} ({$collection->getAttribute('name')})");
|
||||
|
||||
// Collection level
|
||||
$collectionId = $collection->getId() ;
|
||||
$collectionInternalId = $collection->getInternalId();
|
||||
|
||||
$this->migrateUsageMetrics("documents.$databaseId/$collectionId.count.total", "$databaseInternalId.$collectionInternalId.documents");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate Collections.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function migrateCollections(): void
|
||||
{
|
||||
$internalProjectId = $this->project->getInternalId();
|
||||
$collectionType = match ($internalProjectId) {
|
||||
'console' => 'console',
|
||||
default => 'projects',
|
||||
};
|
||||
|
||||
$collections = $this->collections[$collectionType];
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
|
||||
Console::log("Migrating Collection \"{$id}\"");
|
||||
|
||||
$this->projectDB->setNamespace("_$internalProjectId");
|
||||
|
||||
switch ($id) {
|
||||
case 'stats':
|
||||
try {
|
||||
/**
|
||||
* Delete 'type' attribute
|
||||
*/
|
||||
$this->projectDB->deleteAttribute($id, 'type');
|
||||
/**
|
||||
* Alter `signed` internal type on `value` attr
|
||||
*/
|
||||
$this->projectDB->updateAttribute($id, 'value', null, null, null, null, true);
|
||||
$this->projectDB->deleteCachedCollection($id);
|
||||
} catch (Throwable $th) {
|
||||
Console::warning("'type' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrating all Bucket tables.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @throws PDOException
|
||||
*/
|
||||
protected function migrateBuckets(): void
|
||||
{
|
||||
// Project level
|
||||
$this->migrateUsageMetrics('buckets.$all.count.total', 'buckets');
|
||||
$this->migrateUsageMetrics('files.$all.count.total', 'files');
|
||||
$this->migrateUsageMetrics('files.$all.storage.size', 'files.storage');
|
||||
// There is also project.$all.storage.size which is the same as files.$all.storage.size
|
||||
|
||||
foreach ($this->documentsIterator('buckets') as $bucket) {
|
||||
$id = "bucket_{$bucket->getInternalId()}";
|
||||
Console::log("Migrating Bucket {$id} {$bucket->getId()} ({$bucket->getAttribute('name')})");
|
||||
|
||||
// Bucket level
|
||||
$bucketId = $bucket->getId();
|
||||
$bucketInternalId = $bucket->getInternalId();
|
||||
|
||||
$this->migrateUsageMetrics("files.$bucketId.count.total", "$bucketInternalId.files");
|
||||
$this->migrateUsageMetrics("files.$bucketId.storage.size", "$bucketInternalId.files.storage");
|
||||
// some stats come with $ prefix infront of the id -> files.$650c3fda307b7fec4934.storage.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Usage\Calculators\TimeSeries;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database as UtopiaDatabase;
|
||||
use Throwable;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class Usage extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'usage';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->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);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Usage;
|
||||
|
||||
abstract class Calculator
|
||||
{
|
||||
protected string $region;
|
||||
|
||||
public function __construct(string $region)
|
||||
{
|
||||
$this->region = $region;
|
||||
}
|
||||
|
||||
abstract public function collect(): void;
|
||||
}
|
||||
@@ -1,557 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Usage\Calculators;
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Usage\Calculator;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use DateTime;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class TimeSeries extends Calculator
|
||||
{
|
||||
/**
|
||||
* InfluxDB
|
||||
*
|
||||
* @var InfluxDatabase
|
||||
*/
|
||||
protected InfluxDatabase $influxDB;
|
||||
|
||||
/**
|
||||
* Utopia Database
|
||||
*
|
||||
* @var Database
|
||||
*/
|
||||
protected Database $database;
|
||||
|
||||
/**
|
||||
* Error Handler Callback
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* Callback to get project DB
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected mixed $getProjectDB;
|
||||
|
||||
/**
|
||||
* Registry
|
||||
*
|
||||
* @var Registry
|
||||
*/
|
||||
protected Registry $register;
|
||||
|
||||
/**
|
||||
* Latest times for metric that was synced to the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $latestTime = [];
|
||||
|
||||
/**
|
||||
* Periods the metrics are collected for
|
||||
* @var array
|
||||
*/
|
||||
protected array $periods = [
|
||||
[
|
||||
'key' => '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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Usage;
|
||||
|
||||
use Utopia\App;
|
||||
|
||||
class Stats
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params = [];
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $statsd;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'appwrite.usage';
|
||||
|
||||
/**
|
||||
* Event constructor.
|
||||
*
|
||||
* @param mixed $statsd
|
||||
*/
|
||||
public function __construct($statsd)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class UsageBuckets extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('filesCount', [
|
||||
->addRule('filesTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of files in this bucket.',
|
||||
'default' => [],
|
||||
@@ -30,34 +30,6 @@ class UsageBuckets extends Model
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,41 +16,13 @@ class UsageCollection extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('documentsCount', [
|
||||
->addRule('documentsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,72 +16,16 @@ class UsageDatabase extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('documentsCount', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsCount', [
|
||||
->addRule('collectionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of collections.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsCreate', [
|
||||
->addRule('documentsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections delete.',
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
|
||||
@@ -16,111 +16,27 @@ class UsageDatabases extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('databasesCount', [
|
||||
->addRule('databasesTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsCount', [
|
||||
->addRule('collectionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of collections.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsCount', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of collections.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('databasesCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('databasesRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('databasesUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('databasesDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of collections.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collectionsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for collections delete.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,30 +16,16 @@ class UsageFunction extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('executionsTotal', [
|
||||
->addRule('deploymentsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of function executions.',
|
||||
'description' => 'Aggregated stats for number of function deployments.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsFailure', [
|
||||
->addRule('deploymentsStorage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution failures.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsSuccess', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution successes.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution duration.',
|
||||
'description' => 'Aggregated stats for function deployments storage.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
@@ -51,23 +37,31 @@ class UsageFunction extends Model
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsFailure', [
|
||||
->addRule('buildsStorage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function build failures.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsSuccess', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function build successes.',
|
||||
'description' => 'Aggregated stats for builds storage.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function build duration.',
|
||||
'description' => 'Aggregated stats for function build compute.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of function executions.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
|
||||
->addRule('executionsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution compute.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
|
||||
@@ -16,30 +16,23 @@ class UsageFunctions extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('executionsTotal', [
|
||||
->addRule('functionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of function executions.',
|
||||
'description' => 'Aggregated stats for number of functions.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsFailure', [
|
||||
->addRule('deploymentsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution failures.',
|
||||
'description' => 'Aggregated stats for number of function deployments.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsSuccess', [
|
||||
->addRule('deploymentsStorage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution successes.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution duration.',
|
||||
'description' => 'Aggregated stats for function deployments storage.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
@@ -51,23 +44,31 @@ class UsageFunctions extends Model
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsFailure', [
|
||||
->addRule('buildsStorage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function build failures.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsSuccess', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function build successes.',
|
||||
'description' => 'Aggregated stats for builds storage.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function build duration.',
|
||||
'description' => 'Aggregated stats for function build compute.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of function executions.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
|
||||
->addRule('executionsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function execution compute.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
|
||||
@@ -16,7 +16,7 @@ class UsageProject extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('requests', [
|
||||
->addRule('requestsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of requests.',
|
||||
'default' => [],
|
||||
@@ -30,42 +30,42 @@ class UsageProject extends Model
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executions', [
|
||||
->addRule('executionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for function executions.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents', [
|
||||
->addRule('documentsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of documents.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('databases', [
|
||||
->addRule('databasesTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of databases.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users', [
|
||||
->addRule('usersTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of users.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('storage', [
|
||||
->addRule('filesStorage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buckets', [
|
||||
->addRule('bucketsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for number of buckets.',
|
||||
'default' => [],
|
||||
|
||||
@@ -16,79 +16,23 @@ class UsageStorage extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('storage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesCount', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of files.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('bucketsCount', [
|
||||
->addRule('bucketsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of buckets.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('bucketsCreate', [
|
||||
->addRule('filesTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for buckets created.',
|
||||
'description' => 'Aggregated stats for total number of files.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('bucketsRead', [
|
||||
->addRule('filesStorage', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for buckets read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('bucketsUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for buckets updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('bucketsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for buckets deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('filesDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for files deleted.',
|
||||
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
|
||||
@@ -16,62 +16,21 @@ class UsageUsers extends Model
|
||||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('usersCount', [
|
||||
->addRule('usersTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for total number of users.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('usersCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for users created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('usersRead', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for users read.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('usersUpdate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for users updated.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('usersDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for users deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessionsCreate', [
|
||||
|
||||
->addRule('sessionsTotal', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for sessions created.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessionsProviderCreate', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessionsDelete', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated stats for sessions deleted.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class Executor
|
||||
) {
|
||||
$timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
|
||||
|
||||
$runtimeId = "$projectId-$deploymentId-build";
|
||||
$runtimeId = "$projectId-$deploymentId";
|
||||
$route = "/runtimes/{$runtimeId}/logs";
|
||||
$params = [
|
||||
'timeout' => $timeout
|
||||
|
||||
+730
-787
File diff suppressed because it is too large
Load Diff
@@ -193,6 +193,44 @@ class DatabasesConsoleClientTest extends Scope
|
||||
$this->assertEquals($response['body'], "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
public function testGetDatabaseUsage(array $data)
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '32h'
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '24h'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(count($response['body']), 3);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['documentsTotal']);
|
||||
$this->assertIsArray($response['body']['collectionsTotal']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
@@ -230,15 +268,10 @@ class DatabasesConsoleClientTest extends Scope
|
||||
], $this->getHeaders()), [
|
||||
'range' => '24h'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(count($response['body']), 6);
|
||||
$this->assertEquals(count($response['body']), 2);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['documentsCount']);
|
||||
$this->assertIsArray($response['body']['documentsCreate']);
|
||||
$this->assertIsArray($response['body']['documentsRead']);
|
||||
$this->assertIsArray($response['body']['documentsUpdate']);
|
||||
$this->assertIsArray($response['body']['documentsDelete']);
|
||||
$this->assertIsArray($response['body']['documentsTotal']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,7 +56,7 @@ class FunctionsConsoleClientTest extends Scope
|
||||
/**
|
||||
* @depends testCreateFunction
|
||||
*/
|
||||
public function testGetCollectionUsage(array $data)
|
||||
public function testGetFunctionUsage(array $data)
|
||||
{
|
||||
/**
|
||||
* Test for FAILURE
|
||||
@@ -92,16 +92,15 @@ class FunctionsConsoleClientTest extends Scope
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 9);
|
||||
$this->assertEquals(count($response['body']), 8);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['executionsTotal']);
|
||||
$this->assertIsArray($response['body']['executionsFailure']);
|
||||
$this->assertIsArray($response['body']['executionsSuccess']);
|
||||
$this->assertIsArray($response['body']['executionsTime']);
|
||||
$this->assertIsArray($response['body']['deploymentsTotal']);
|
||||
$this->assertIsArray($response['body']['deploymentsStorage']);
|
||||
$this->assertIsArray($response['body']['buildsTotal']);
|
||||
$this->assertIsArray($response['body']['buildsFailure']);
|
||||
$this->assertIsArray($response['body']['buildsSuccess']);
|
||||
$this->assertIsArray($response['body']['buildsStorage']);
|
||||
$this->assertIsArray($response['body']['buildsTime']);
|
||||
$this->assertIsArray($response['body']['executionsTotal']);
|
||||
$this->assertIsArray($response['body']['executionsTime']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -445,7 +445,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/usage', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/project/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
@@ -454,14 +454,14 @@ class ProjectsConsoleClientTest extends Scope
|
||||
$this->assertEquals(count($response['body']), 9);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEquals('30d', $response['body']['range']);
|
||||
$this->assertIsArray($response['body']['requests']);
|
||||
$this->assertIsArray($response['body']['requestsTotal']);
|
||||
$this->assertIsArray($response['body']['network']);
|
||||
$this->assertIsArray($response['body']['executions']);
|
||||
$this->assertIsArray($response['body']['documents']);
|
||||
$this->assertIsArray($response['body']['databases']);
|
||||
$this->assertIsArray($response['body']['buckets']);
|
||||
$this->assertIsArray($response['body']['users']);
|
||||
$this->assertIsArray($response['body']['storage']);
|
||||
$this->assertIsArray($response['body']['executionsTotal']);
|
||||
$this->assertIsArray($response['body']['documentsTotal']);
|
||||
$this->assertIsArray($response['body']['databasesTotal']);
|
||||
$this->assertIsArray($response['body']['bucketsTotal']);
|
||||
$this->assertIsArray($response['body']['usersTotal']);
|
||||
$this->assertIsArray($response['body']['filesStorage']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
||||
@@ -39,10 +39,11 @@ class StorageConsoleClientTest extends Scope
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(12, count($response['body']));
|
||||
$this->assertEquals(4, count($response['body']));
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['storage']);
|
||||
$this->assertIsArray($response['body']['filesCount']);
|
||||
$this->assertIsArray($response['body']['bucketsTotal']);
|
||||
$this->assertIsArray($response['body']['filesTotal']);
|
||||
$this->assertIsArray($response['body']['filesStorage']);
|
||||
}
|
||||
|
||||
public function testGetStorageBucketUsage()
|
||||
@@ -94,13 +95,9 @@ class StorageConsoleClientTest extends Scope
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 7);
|
||||
$this->assertEquals(count($response['body']), 3);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['filesCount']);
|
||||
$this->assertIsArray($response['body']['filesCreate']);
|
||||
$this->assertIsArray($response['body']['filesRead']);
|
||||
$this->assertIsArray($response['body']['filesUpdate']);
|
||||
$this->assertIsArray($response['body']['filesDelete']);
|
||||
$this->assertIsArray($response['body']['filesTotal']);
|
||||
$this->assertIsArray($response['body']['filesStorage']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,17 +23,6 @@ class UsersConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '32h',
|
||||
'provider' => 'email'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '24h',
|
||||
'provider' => 'some-random-provider'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
@@ -46,38 +35,12 @@ class UsersConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '24h',
|
||||
'provider' => 'email'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 9);
|
||||
$this->assertEquals(count($response['body']), 3);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['usersCount']);
|
||||
$this->assertIsArray($response['body']['usersCreate']);
|
||||
$this->assertIsArray($response['body']['usersRead']);
|
||||
$this->assertIsArray($response['body']['usersUpdate']);
|
||||
$this->assertIsArray($response['body']['usersDelete']);
|
||||
$this->assertIsArray($response['body']['sessionsCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsProviderCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsDelete']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '24h'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 9);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['usersCount']);
|
||||
$this->assertIsArray($response['body']['usersCreate']);
|
||||
$this->assertIsArray($response['body']['usersRead']);
|
||||
$this->assertIsArray($response['body']['usersUpdate']);
|
||||
$this->assertIsArray($response['body']['usersDelete']);
|
||||
$this->assertIsArray($response['body']['sessionsCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsProviderCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsDelete']);
|
||||
$this->assertIsArray($response['body']['usersTotal']);
|
||||
$this->assertIsArray($response['body']['sessionsTotal']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,70 +2,53 @@
|
||||
|
||||
namespace Tests\Unit\Usage;
|
||||
|
||||
use Appwrite\Usage\Stats;
|
||||
use Appwrite\URL\URL as AppwriteURL;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\App;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Queue;
|
||||
use Utopia\Queue\Client;
|
||||
use Utopia\Queue\Connection;
|
||||
|
||||
class StatsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Stats
|
||||
*/
|
||||
protected $object = null;
|
||||
protected ?Connection $connection = null;
|
||||
protected ?Client $client = null;
|
||||
|
||||
protected const QUEUE_NAME = 'usage-test-q';
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$host = App::getEnv('_APP_STATSD_HOST', 'telegraf');
|
||||
$port = App::getEnv('_APP_STATSD_PORT', 8125);
|
||||
$env = App::getEnv('_APP_CONNECTIONS_QUEUE', AppwriteURL::unparse([
|
||||
'scheme' => 'redis',
|
||||
'host' => App::getEnv('_APP_REDIS_HOST', 'redis'),
|
||||
'port' => App::getEnv('_APP_REDIS_PORT', '6379'),
|
||||
'user' => App::getEnv('_APP_REDIS_USER', ''),
|
||||
'pass' => App::getEnv('_APP_REDIS_PASS', ''),
|
||||
]));
|
||||
|
||||
$connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port);
|
||||
$statsd = new \Domnikl\Statsd\Client($connection);
|
||||
|
||||
$this->object = new Stats($statsd);
|
||||
$dsn = explode('=', $env);
|
||||
$dsn = count($dsn) > 1 ? $dsn[1] : $dsn[0];
|
||||
$dsn = new DSN($dsn);
|
||||
$this->connection = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort());
|
||||
$this->client = new Client(self::QUEUE_NAME, $this->connection);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testNamespace(): void
|
||||
public function testSamePayload(): void
|
||||
{
|
||||
$this->object->setNamespace('appwritetest.usage');
|
||||
$this->assertEquals('appwritetest.usage', $this->object->getNamespace());
|
||||
}
|
||||
$inToQueue = [
|
||||
'key_1' => 'value_1',
|
||||
'key_2' => 'value_2',
|
||||
];
|
||||
|
||||
public function testParams(): void
|
||||
{
|
||||
$this->object
|
||||
->setParam('projectId', 'appwrite_test')
|
||||
->setParam('projectInternalId', 1)
|
||||
->setParam('networkRequestSize', 100)
|
||||
;
|
||||
|
||||
$this->assertEquals('appwrite_test', $this->object->getParam('projectId'));
|
||||
$this->assertEquals(1, $this->object->getParam('projectInternalId'));
|
||||
$this->assertEquals(100, $this->object->getParam('networkRequestSize'));
|
||||
|
||||
$this->object->submit();
|
||||
|
||||
$this->assertEquals(null, $this->object->getParam('projectId'));
|
||||
$this->assertEquals(null, $this->object->getParam('networkRequestSize'));
|
||||
}
|
||||
|
||||
public function testReset(): void
|
||||
{
|
||||
$this->object
|
||||
->setParam('projectId', 'appwrite_test')
|
||||
->setParam('networkRequestSize', 100)
|
||||
;
|
||||
|
||||
$this->assertEquals('appwrite_test', $this->object->getParam('projectId'));
|
||||
$this->assertEquals(100, $this->object->getParam('networkRequestSize'));
|
||||
|
||||
$this->object->reset();
|
||||
|
||||
$this->assertEquals(null, $this->object->getParam('projectId'));
|
||||
$this->assertEquals(null, $this->object->getParam('networkRequestSize'));
|
||||
$this->assertEquals('appwrite.usage', $this->object->getNamespace());
|
||||
$result = $this->client->enqueue($inToQueue);
|
||||
$this->assertTrue($result);
|
||||
$outFromQueue = $this->connection->leftPopArray('utopia-queue.queue.' . self::QUEUE_NAME, 0)['payload'];
|
||||
$this->assertNotEmpty($outFromQueue);
|
||||
$this->assertSame($inToQueue, $outFromQueue);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user