Merge remote-tracking branch 'origin/feat-database-indexing' into feat-enforce-document-collection-permissions
This commit is contained in:
commit
5735c5d129
|
@ -225,6 +225,7 @@ RUN mkdir -p /storage/uploads && \
|
||||||
# Executables
|
# Executables
|
||||||
RUN chmod +x /usr/local/bin/doctor && \
|
RUN chmod +x /usr/local/bin/doctor && \
|
||||||
chmod +x /usr/local/bin/maintenance && \
|
chmod +x /usr/local/bin/maintenance && \
|
||||||
|
chmod +x /usr/local/bin/usage && \
|
||||||
chmod +x /usr/local/bin/install && \
|
chmod +x /usr/local/bin/install && \
|
||||||
chmod +x /usr/local/bin/migrate && \
|
chmod +x /usr/local/bin/migrate && \
|
||||||
chmod +x /usr/local/bin/schedule && \
|
chmod +x /usr/local/bin/schedule && \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once __DIR__.'/workers.php';
|
require_once __DIR__.'/init.php';
|
||||||
|
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
use Utopia\CLI\CLI;
|
use Utopia\CLI\CLI;
|
||||||
|
@ -15,6 +15,7 @@ include 'tasks/migrate.php';
|
||||||
include 'tasks/sdks.php';
|
include 'tasks/sdks.php';
|
||||||
include 'tasks/ssl.php';
|
include 'tasks/ssl.php';
|
||||||
include 'tasks/vars.php';
|
include 'tasks/vars.php';
|
||||||
|
include 'tasks/usage.php';
|
||||||
|
|
||||||
$cli
|
$cli
|
||||||
->task('version')
|
->task('version')
|
||||||
|
|
|
@ -1728,6 +1728,91 @@ $collections = [
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'stats' => [
|
||||||
|
'$collection' => Database::METADATA,
|
||||||
|
'$id' => 'stats',
|
||||||
|
'name' => 'Stats',
|
||||||
|
'attributes' => [
|
||||||
|
[
|
||||||
|
'$id' => 'metric',
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 255,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => 'value',
|
||||||
|
'type' => Database::VAR_INTEGER,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'signed' => false,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => 'time',
|
||||||
|
'type' => Database::VAR_INTEGER,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'signed' => false,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => 'period',
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 4,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => 'type',
|
||||||
|
'type' => Database::VAR_INTEGER,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 1,
|
||||||
|
'signed' => false,
|
||||||
|
'required' => true,
|
||||||
|
'default' => 0, // 0 -> count, 1 -> sum
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'indexes' => [
|
||||||
|
[
|
||||||
|
'$id' => '_key_time',
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['time'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_DESC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => '_key_metric',
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['metric'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => '_key_metric_period',
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['metric', 'period'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_DESC],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
return $collections;
|
return $collections;
|
||||||
|
|
|
@ -149,6 +149,15 @@ return [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'question' => '',
|
'question' => '',
|
||||||
'filter' => ''
|
'filter' => ''
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => '_APP_USAGE_AGGREGATION_INTERVAL',
|
||||||
|
'description' => 'Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds.',
|
||||||
|
'introduction' => '0.10.0',
|
||||||
|
'default' => '30',
|
||||||
|
'required' => false,
|
||||||
|
'question' => '',
|
||||||
|
'filter' => ''
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
|
@ -52,12 +52,14 @@ App::post('/v1/account')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($userId, $email, $password, $name, $request, $response, $project, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $email, $password, $name, $request, $response, $project, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$email = \strtolower($email);
|
$email = \strtolower($email);
|
||||||
if ('console' === $project->getId()) {
|
if ('console' === $project->getId()) {
|
||||||
|
@ -120,6 +122,9 @@ App::post('/v1/account')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.create', 1)
|
||||||
|
;
|
||||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
@ -147,13 +152,15 @@ App::post('/v1/account/sessions')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($email, $password, $request, $response, $dbForInternal, $locale, $geodb, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($email, $password, $request, $response, $dbForInternal, $locale, $geodb, $audits, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
/** @var MaxMind\Db\Reader $geodb */
|
/** @var MaxMind\Db\Reader $geodb */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$email = \strtolower($email);
|
$email = \strtolower($email);
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
|
@ -227,6 +234,11 @@ App::post('/v1/account/sessions')
|
||||||
->setAttribute('countryName', $countryName)
|
->setAttribute('countryName', $countryName)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
->setParam('users.sessions.create', 1)
|
||||||
|
->setParam('provider', 'email')
|
||||||
|
;
|
||||||
$response->dynamic($session, Response::MODEL_SESSION);
|
$response->dynamic($session, Response::MODEL_SESSION);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -357,7 +369,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($provider, $code, $state, $request, $response, $project, $user, $dbForInternal, $geodb, $audits, $events) use ($oauthDefaultSuccess) {
|
->inject('usage')
|
||||||
|
->action(function ($provider, $code, $state, $request, $response, $project, $user, $dbForInternal, $geodb, $audits, $events, $usage) use ($oauthDefaultSuccess) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
|
@ -365,6 +378,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var MaxMind\Db\Reader $geodb */
|
/** @var MaxMind\Db\Reader $geodb */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||||
|
@ -545,6 +559,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
|
|
||||||
$events->setParam('eventData', $response->output($session, Response::MODEL_SESSION));
|
$events->setParam('eventData', $response->output($session, Response::MODEL_SESSION));
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.sessions.create', 1)
|
||||||
|
->setParam('projectId', $project->getId())
|
||||||
|
->setParam('provider', 'oauth2-'.$provider)
|
||||||
|
;
|
||||||
if (!Config::getParam('domainVerification')) {
|
if (!Config::getParam('domainVerification')) {
|
||||||
$response
|
$response
|
||||||
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
|
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
|
||||||
|
@ -595,7 +614,8 @@ App::post('/v1/account/sessions/anonymous')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($request, $response, $locale, $user, $project, $dbForInternal, $geodb, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($request, $response, $locale, $user, $project, $dbForInternal, $geodb, $audits, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
|
@ -604,6 +624,7 @@ App::post('/v1/account/sessions/anonymous')
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var MaxMind\Db\Reader $geodb */
|
/** @var MaxMind\Db\Reader $geodb */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
|
|
||||||
|
@ -686,6 +707,11 @@ App::post('/v1/account/sessions/anonymous')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.sessions.create', 1)
|
||||||
|
->setParam('provider', 'anonymous')
|
||||||
|
;
|
||||||
|
|
||||||
if (!Config::getParam('domainVerification')) {
|
if (!Config::getParam('domainVerification')) {
|
||||||
$response
|
$response
|
||||||
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
|
->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
|
||||||
|
@ -771,10 +797,15 @@ App::get('/v1/account')
|
||||||
->label('sdk.response.model', Response::MODEL_USER)
|
->label('sdk.response.model', Response::MODEL_USER)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->action(function ($response, $user) {
|
->inject('usage')
|
||||||
|
->action(function ($response, $user, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -791,12 +822,17 @@ App::get('/v1/account/prefs')
|
||||||
->label('sdk.response.model', Response::MODEL_PREFERENCES)
|
->label('sdk.response.model', Response::MODEL_PREFERENCES)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->action(function ($response, $user) {
|
->inject('usage')
|
||||||
|
->action(function ($response, $user, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$prefs = $user->getAttribute('prefs', new \stdClass());
|
$prefs = $user->getAttribute('prefs', new \stdClass());
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -814,10 +850,12 @@ App::get('/v1/account/sessions')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->action(function ($response, $user, $locale) {
|
->inject('usage')
|
||||||
|
->action(function ($response, $user, $locale, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
$current = Auth::sessionVerify($sessions, Auth::$secret);
|
$current = Auth::sessionVerify($sessions, Auth::$secret);
|
||||||
|
@ -831,6 +869,9 @@ App::get('/v1/account/sessions')
|
||||||
$sessions[$key] = $session;
|
$sessions[$key] = $session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'sessions' => $sessions,
|
'sessions' => $sessions,
|
||||||
'sum' => count($sessions),
|
'sum' => count($sessions),
|
||||||
|
@ -853,13 +894,15 @@ App::get('/v1/account/logs')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($response, $user, $locale, $geodb, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($response, $user, $locale, $geodb, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
/** @var MaxMind\Db\Reader $geodb */
|
/** @var MaxMind\Db\Reader $geodb */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$audit = new Audit($dbForInternal);
|
$audit = new Audit($dbForInternal);
|
||||||
|
|
||||||
|
@ -906,6 +949,9 @@ App::get('/v1/account/logs')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -925,11 +971,13 @@ App::get('/v1/account/sessions/:sessionId')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($sessionId, $response, $user, $locale, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($sessionId, $response, $user, $locale, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
$sessionId = ($sessionId === 'current')
|
$sessionId = ($sessionId === 'current')
|
||||||
|
@ -948,6 +996,10 @@ App::get('/v1/account/sessions/:sessionId')
|
||||||
->setAttribute('countryName', $countryName)
|
->setAttribute('countryName', $countryName)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
|
|
||||||
return $response->dynamic($session, Response::MODEL_SESSION);
|
return $response->dynamic($session, Response::MODEL_SESSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -972,11 +1024,13 @@ App::patch('/v1/account/name')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($name, $response, $user, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($name, $response, $user, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
|
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
|
||||||
|
|
||||||
|
@ -986,6 +1040,10 @@ App::patch('/v1/account/name')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
|
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1007,11 +1065,13 @@ App::patch('/v1/account/password')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($password, $oldPassword, $response, $user, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($password, $oldPassword, $response, $user, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
// Check old password only if its an existing user.
|
// Check old password only if its an existing user.
|
||||||
if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password
|
if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password
|
||||||
|
@ -1029,6 +1089,9 @@ App::patch('/v1/account/password')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1050,11 +1113,13 @@ App::patch('/v1/account/email')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($email, $password, $response, $user, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($email, $password, $response, $user, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting
|
$isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting
|
||||||
|
|
||||||
|
@ -1082,6 +1147,9 @@ App::patch('/v1/account/email')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1102,11 +1170,13 @@ App::patch('/v1/account/prefs')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($prefs, $response, $user, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($prefs, $response, $user, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
||||||
|
|
||||||
|
@ -1115,6 +1185,9 @@ App::patch('/v1/account/prefs')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1135,13 +1208,15 @@ App::delete('/v1/account')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($request, $response, $user, $dbForInternal, $audits, $events) {
|
->inject('usage')
|
||||||
|
->action(function ($request, $response, $user, $dbForInternal, $audits, $events, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
|
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
|
||||||
|
@ -1171,6 +1246,9 @@ App::delete('/v1/account')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.delete', 1)
|
||||||
|
;
|
||||||
$response
|
$response
|
||||||
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||||
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||||
|
@ -1198,7 +1276,8 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($sessionId, $request, $response, $user, $dbForInternal, $locale, $audits, $events) {
|
->inject('usage')
|
||||||
|
->action(function ($sessionId, $request, $response, $user, $dbForInternal, $locale, $audits, $events, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
|
@ -1206,6 +1285,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$sessionId = ($sessionId === 'current')
|
$sessionId = ($sessionId === 'current')
|
||||||
|
@ -1252,6 +1332,10 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||||
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
|
->setParam('eventData', $response->output($session, Response::MODEL_SESSION))
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.sessions.delete', 1)
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
return $response->noContent();
|
return $response->noContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1362,8 @@ App::delete('/v1/account/sessions')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($request, $response, $user, $dbForInternal, $locale, $audits, $events) {
|
->inject('usage')
|
||||||
|
->action(function ($request, $response, $user, $dbForInternal, $locale, $audits, $events, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
|
@ -1286,6 +1371,7 @@ App::delete('/v1/account/sessions')
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
@ -1321,13 +1407,19 @@ App::delete('/v1/account/sessions')
|
||||||
|
|
||||||
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
|
$dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', []));
|
||||||
|
|
||||||
|
$numOfSessions = count($sessions);
|
||||||
|
|
||||||
$events
|
$events
|
||||||
->setParam('eventData', $response->output(new Document([
|
->setParam('eventData', $response->output(new Document([
|
||||||
'sessions' => $sessions,
|
'sessions' => $sessions,
|
||||||
'sum' => count($sessions),
|
'sum' => $numOfSessions,
|
||||||
]), Response::MODEL_SESSION_LIST))
|
]), Response::MODEL_SESSION_LIST))
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.sessions.delete', $numOfSessions)
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1355,7 +1447,8 @@ App::post('/v1/account/recovery')
|
||||||
->inject('mails')
|
->inject('mails')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($email, $url, $request, $response, $dbForInternal, $project, $locale, $mails, $audits, $events) {
|
->inject('usage')
|
||||||
|
->action(function ($email, $url, $request, $response, $dbForInternal, $project, $locale, $mails, $audits, $events, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
@ -1364,6 +1457,7 @@ App::post('/v1/account/recovery')
|
||||||
/** @var Appwrite\Event\Event $mails */
|
/** @var Appwrite\Event\Event $mails */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||||
|
@ -1431,6 +1525,9 @@ App::post('/v1/account/recovery')
|
||||||
->setParam('resource', 'user/' . $profile->getId())
|
->setParam('resource', 'user/' . $profile->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||||
$response->dynamic($recovery, Response::MODEL_TOKEN);
|
$response->dynamic($recovery, Response::MODEL_TOKEN);
|
||||||
});
|
});
|
||||||
|
@ -1456,10 +1553,12 @@ App::put('/v1/account/recovery')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($userId, $secret, $password, $passwordAgain, $response, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $secret, $password, $passwordAgain, $response, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
if ($password !== $passwordAgain) {
|
if ($password !== $passwordAgain) {
|
||||||
throw new Exception('Passwords must match', 400);
|
throw new Exception('Passwords must match', 400);
|
||||||
|
@ -1506,6 +1605,9 @@ App::put('/v1/account/recovery')
|
||||||
->setParam('resource', 'user/' . $profile->getId())
|
->setParam('resource', 'user/' . $profile->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($recovery, Response::MODEL_TOKEN);
|
$response->dynamic($recovery, Response::MODEL_TOKEN);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1533,7 +1635,8 @@ App::post('/v1/account/verification')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->inject('mails')
|
->inject('mails')
|
||||||
->action(function ($url, $request, $response, $project, $user, $dbForInternal, $locale, $audits, $events, $mails) {
|
->inject('usage')
|
||||||
|
->action(function ($url, $request, $response, $project, $user, $dbForInternal, $locale, $audits, $events, $mails, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
|
@ -1543,6 +1646,7 @@ App::post('/v1/account/verification')
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $mails */
|
/** @var Appwrite\Event\Event $mails */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||||
|
@ -1600,6 +1704,9 @@ App::post('/v1/account/verification')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||||
});
|
});
|
||||||
|
@ -1624,11 +1731,13 @@ App::put('/v1/account/verification')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($userId, $secret, $response, $user, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $secret, $response, $user, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$profile = $dbForInternal->getDocument('users', $userId);
|
$profile = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -1666,5 +1775,8 @@ App::put('/v1/account/verification')
|
||||||
->setParam('resource', 'user/' . $user->getId())
|
->setParam('resource', 'user/' . $user->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,12 +30,13 @@ use Appwrite\Utopia\Response;
|
||||||
use Appwrite\Database\Validator\CustomId;
|
use Appwrite\Database\Validator\CustomId;
|
||||||
use DeviceDetector\DeviceDetector;
|
use DeviceDetector\DeviceDetector;
|
||||||
|
|
||||||
$attributesCallback = function ($collectionId, $attribute, $response, $dbForInternal, $database, $audits) {
|
$attributesCallback = function ($collectionId, $attribute, $response, $dbForInternal, $database, $audits, $usage) {
|
||||||
/** @var Utopia\Database\Document $attribute*/
|
/** @var Utopia\Database\Document $attribute*/
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$attributeId = $attribute->getId();
|
$attributeId = $attribute->getId();
|
||||||
$type = $attribute->getAttribute('type', '');
|
$type = $attribute->getAttribute('type', '');
|
||||||
|
@ -100,6 +101,8 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte
|
||||||
->setParam('document', $attribute)
|
->setParam('document', $attribute)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.update', 1);
|
||||||
|
|
||||||
$audits
|
$audits
|
||||||
->setParam('event', 'database.attributes.create')
|
->setParam('event', 'database.attributes.create')
|
||||||
->setParam('resource', 'collection/'.$collection->getId())
|
->setParam('resource', 'collection/'.$collection->getId())
|
||||||
|
@ -131,10 +134,12 @@ App::post('/v1/database/collections')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('dbForExternal')
|
->inject('dbForExternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForInternal, $dbForExternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForExternal*/
|
/** @var Utopia\Database\Database $dbForExternal*/
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collectionId = $collectionId == 'unique()' ? $dbForExternal->getId() : $collectionId;
|
$collectionId = $collectionId == 'unique()' ? $dbForExternal->getId() : $collectionId;
|
||||||
|
|
||||||
|
@ -161,6 +166,8 @@ App::post('/v1/database/collections')
|
||||||
->setParam('data', $collection->getArrayCopy())
|
->setParam('data', $collection->getArrayCopy())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.create', 1);
|
||||||
|
|
||||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||||
$response->dynamic($collection, Response::MODEL_COLLECTION);
|
$response->dynamic($collection, Response::MODEL_COLLECTION);
|
||||||
});
|
});
|
||||||
|
@ -183,7 +190,8 @@ App::get('/v1/database/collections')
|
||||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
@ -201,6 +209,8 @@ App::get('/v1/database/collections')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.read', 1);
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'collections' => $dbForInternal->find('collections', $queries, $limit, $offset, [], [$orderType], $afterCollection ?? null),
|
'collections' => $dbForInternal->find('collections', $queries, $limit, $offset, [], [$orderType], $afterCollection ?? null),
|
||||||
'sum' => $dbForInternal->count('collections', $queries, APP_LIMIT_COUNT),
|
'sum' => $dbForInternal->count('collections', $queries, APP_LIMIT_COUNT),
|
||||||
|
@ -221,7 +231,8 @@ App::get('/v1/database/collections/:collectionId')
|
||||||
->param('collectionId', '', new UID(), 'Collection unique ID.')
|
->param('collectionId', '', new UID(), 'Collection unique ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($collectionId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
@ -231,9 +242,191 @@ App::get('/v1/database/collections/:collectionId')
|
||||||
throw new Exception('Collection not found', 404);
|
throw new Exception('Collection not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.read', 1);
|
||||||
|
|
||||||
$response->dynamic($collection, Response::MODEL_COLLECTION);
|
$response->dynamic($collection, Response::MODEL_COLLECTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::get('/v1/database/usage')
|
||||||
|
->desc('Get usage stats for the database')
|
||||||
|
->groups(['api', 'database'])
|
||||||
|
->label('scope', 'collections.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'database')
|
||||||
|
->label('sdk.method', 'getUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_DATABASE)
|
||||||
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForInternal')
|
||||||
|
->action(function ($range, $response, $dbForInternal) {
|
||||||
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
|
/** @var Utopia\Database\Database $dbForConsole */
|
||||||
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Utopia\Registry\Registry $register */
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
|
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
|
$period = [
|
||||||
|
'24h' => [
|
||||||
|
'period' => '30m',
|
||||||
|
'limit' => 48,
|
||||||
|
],
|
||||||
|
'7d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 7,
|
||||||
|
],
|
||||||
|
'30d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 30,
|
||||||
|
],
|
||||||
|
'90d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 90,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$metrics = [
|
||||||
|
'database.documents.count',
|
||||||
|
'database.collections.count',
|
||||||
|
'database.collections.create',
|
||||||
|
'database.collections.read',
|
||||||
|
'database.collections.update',
|
||||||
|
'database.collections.delete',
|
||||||
|
'database.documents.create',
|
||||||
|
'database.documents.read',
|
||||||
|
'database.documents.update',
|
||||||
|
'database.documents.delete'
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [];
|
||||||
|
|
||||||
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
|
foreach ($metrics as $metric) {
|
||||||
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
|
$stats[$metric] = [];
|
||||||
|
foreach ($requestDocs as $requestDoc) {
|
||||||
|
$stats[$metric][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$usage = new Document([
|
||||||
|
'range' => $range,
|
||||||
|
'documents.count' => $stats["database.documents.count"],
|
||||||
|
'collections.count' => $stats["database.collections.count"],
|
||||||
|
'documents.create' => $stats["database.documents.create"],
|
||||||
|
'documents.read' => $stats["database.documents.read"],
|
||||||
|
'documents.update' => $stats["database.documents.update"],
|
||||||
|
'documents.delete' => $stats["database.documents.delete"],
|
||||||
|
'collections.create' => $stats["database.collections.create"],
|
||||||
|
'collections.read' => $stats["database.collections.read"],
|
||||||
|
'collections.update' => $stats["database.collections.update"],
|
||||||
|
'collections.delete' => $stats["database.collections.delete"],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_DATABASE);
|
||||||
|
});
|
||||||
|
|
||||||
|
App::get('/v1/database/:collectionId/usage')
|
||||||
|
->desc('Get usage stats for a collection')
|
||||||
|
->groups(['api', 'database'])
|
||||||
|
->label('scope', 'collections.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'database')
|
||||||
|
->label('sdk.method', 'getCollectionUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_COLLECTION)
|
||||||
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
|
->param('collectionId', '', new UID(), 'Collection unique ID.')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForInternal')
|
||||||
|
->inject('dbForExternal')
|
||||||
|
->action(function ($range, $collectionId, $response, $dbForInternal, $dbForExternal) {
|
||||||
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
|
/** @var Utopia\Database\Database $dbForConsole */
|
||||||
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Utopia\Registry\Registry $register */
|
||||||
|
|
||||||
|
$collection = $dbForExternal->getCollection($collectionId);
|
||||||
|
|
||||||
|
if ($collection->isEmpty()) {
|
||||||
|
throw new Exception('Collection not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
|
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
|
$period = [
|
||||||
|
'24h' => [
|
||||||
|
'period' => '30m',
|
||||||
|
'limit' => 48,
|
||||||
|
],
|
||||||
|
'7d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 7,
|
||||||
|
],
|
||||||
|
'30d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 30,
|
||||||
|
],
|
||||||
|
'90d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 90,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$metrics = [
|
||||||
|
"database.collections.$collectionId.documents.count",
|
||||||
|
"database.collections.$collectionId.documents.create",
|
||||||
|
"database.collections.$collectionId.documents.read",
|
||||||
|
"database.collections.$collectionId.documents.update",
|
||||||
|
"database.collections.$collectionId.documents.delete",
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [];
|
||||||
|
|
||||||
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
|
foreach ($metrics as $metric) {
|
||||||
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
|
$stats[$metric] = [];
|
||||||
|
foreach ($requestDocs as $requestDoc) {
|
||||||
|
$stats[$metric][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$usage = new Document([
|
||||||
|
'range' => $range,
|
||||||
|
'documents.count' => $stats["database.collections.$collectionId.documents.count"],
|
||||||
|
'documents.create' => $stats["database.collections.$collectionId.documents.create"],
|
||||||
|
'documents.read' => $stats["database.collections.$collectionId.documents.read"],
|
||||||
|
'documents.update' => $stats["database.collections.$collectionId.documents.update"],
|
||||||
|
'documents.delete' => $stats["database.collections.$collectionId.documents.delete"]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_COLLECTION);
|
||||||
|
});
|
||||||
|
|
||||||
App::get('/v1/database/collections/:collectionId/logs')
|
App::get('/v1/database/collections/:collectionId/logs')
|
||||||
->desc('List Collection Logs')
|
->desc('List Collection Logs')
|
||||||
->groups(['api', 'database'])
|
->groups(['api', 'database'])
|
||||||
|
@ -350,10 +543,12 @@ App::put('/v1/database/collections/:collectionId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -381,6 +576,8 @@ App::put('/v1/database/collections/:collectionId')
|
||||||
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
|
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.update', 1);
|
||||||
|
|
||||||
$audits
|
$audits
|
||||||
->setParam('event', 'database.collections.update')
|
->setParam('event', 'database.collections.update')
|
||||||
->setParam('resource', 'collection/'.$collection->getId())
|
->setParam('resource', 'collection/'.$collection->getId())
|
||||||
|
@ -408,12 +605,14 @@ App::delete('/v1/database/collections/:collectionId')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->inject('deletes')
|
->inject('deletes')
|
||||||
->action(function ($collectionId, $response, $dbForInternal, $dbForExternal, $events, $audits, $deletes) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $response, $dbForInternal, $dbForExternal, $events, $audits, $deletes, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Database\Database $dbForExternal */
|
/** @var Utopia\Database\Database $dbForExternal */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $audits */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -427,6 +626,8 @@ App::delete('/v1/database/collections/:collectionId')
|
||||||
throw new Exception('Failed to remove collection from DB', 500);
|
throw new Exception('Failed to remove collection from DB', 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.delete', 1);
|
||||||
|
|
||||||
$events
|
$events
|
||||||
->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION))
|
->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION))
|
||||||
;
|
;
|
||||||
|
@ -462,12 +663,13 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Utopia\Database\Database $dbForExternal*/
|
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -476,7 +678,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
|
||||||
'required' => $required,
|
'required' => $required,
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/database/collections/:collectionId/attributes/email')
|
App::post('/v1/database/collections/:collectionId/attributes/email')
|
||||||
|
@ -500,11 +702,13 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -514,7 +718,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
'format' => 'email',
|
'format' => 'email',
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/database/collections/:collectionId/attributes/ip')
|
App::post('/v1/database/collections/:collectionId/attributes/ip')
|
||||||
|
@ -538,11 +742,13 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -552,7 +758,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
'format' => 'ip',
|
'format' => 'ip',
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/database/collections/:collectionId/attributes/url')
|
App::post('/v1/database/collections/:collectionId/attributes/url')
|
||||||
|
@ -576,11 +782,13 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForExternal*/
|
/** @var Utopia\Database\Database $dbForExternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -590,7 +798,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
'format' => 'url',
|
'format' => 'url',
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/database/collections/:collectionId/attributes/integer')
|
App::post('/v1/database/collections/:collectionId/attributes/integer')
|
||||||
|
@ -616,11 +824,13 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -634,7 +844,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
|
||||||
'min' => (is_null($min)) ? PHP_INT_MIN : \intval($min),
|
'min' => (is_null($min)) ? PHP_INT_MIN : \intval($min),
|
||||||
'max' => (is_null($max)) ? PHP_INT_MAX : \intval($max),
|
'max' => (is_null($max)) ? PHP_INT_MAX : \intval($max),
|
||||||
],
|
],
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/database/collections/:collectionId/attributes/float')
|
App::post('/v1/database/collections/:collectionId/attributes/float')
|
||||||
|
@ -660,11 +870,13 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -678,7 +890,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
||||||
'min' => (is_null($min)) ? PHP_FLOAT_MIN : \floatval($min),
|
'min' => (is_null($min)) ? PHP_FLOAT_MIN : \floatval($min),
|
||||||
'max' => (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max),
|
'max' => (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max),
|
||||||
],
|
],
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
||||||
|
@ -702,11 +914,13 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits) use ($attributesCallback) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) use ($attributesCallback) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal*/
|
/** @var Utopia\Database\Database $dbForInternal*/
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
return $attributesCallback($collectionId, new Document([
|
return $attributesCallback($collectionId, new Document([
|
||||||
'$id' => $attributeId,
|
'$id' => $attributeId,
|
||||||
|
@ -715,7 +929,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
||||||
'required' => $required,
|
'required' => $required,
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
]), $response, $dbForInternal, $database, $audits);
|
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/database/collections/:collectionId/attributes')
|
App::get('/v1/database/collections/:collectionId/attributes')
|
||||||
|
@ -732,7 +946,8 @@ App::get('/v1/database/collections/:collectionId/attributes')
|
||||||
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
|
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($collectionId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
@ -750,6 +965,8 @@ App::get('/v1/database/collections/:collectionId/attributes')
|
||||||
])]);
|
])]);
|
||||||
}, $attributes);
|
}, $attributes);
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.read', 1);
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'sum' => \count($attributes),
|
'sum' => \count($attributes),
|
||||||
'attributes' => $attributes
|
'attributes' => $attributes
|
||||||
|
@ -771,7 +988,8 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
|
||||||
->param('attributeId', '', new Key(), 'Attribute ID.')
|
->param('attributeId', '', new Key(), 'Attribute ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($collectionId, $attributeId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
@ -794,6 +1012,8 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
|
||||||
'collectionId' => $collectionId,
|
'collectionId' => $collectionId,
|
||||||
])]);
|
])]);
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.read', 1);
|
||||||
|
|
||||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE);
|
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -815,12 +1035,14 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $database, $events, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $database, $events, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -843,6 +1065,8 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
|
||||||
->setParam('document', $attribute)
|
->setParam('document', $attribute)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.update', 1);
|
||||||
|
|
||||||
$events
|
$events
|
||||||
->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE))
|
->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE))
|
||||||
;
|
;
|
||||||
|
@ -877,11 +1101,13 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $indexId, $type, $attributes, $orders, $response, $dbForInternal, $database, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $indexId, $type, $attributes, $orders, $response, $dbForInternal, $database, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $audits */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -946,6 +1172,8 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
||||||
->setParam('document', $index)
|
->setParam('document', $index)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.update', 1);
|
||||||
|
|
||||||
$audits
|
$audits
|
||||||
->setParam('event', 'database.indexes.create')
|
->setParam('event', 'database.indexes.create')
|
||||||
->setParam('resource', 'collection/'.$collection->getId())
|
->setParam('resource', 'collection/'.$collection->getId())
|
||||||
|
@ -970,7 +1198,8 @@ App::get('/v1/database/collections/:collectionId/indexes')
|
||||||
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
|
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($collectionId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
@ -988,6 +1217,8 @@ App::get('/v1/database/collections/:collectionId/indexes')
|
||||||
])]);
|
])]);
|
||||||
}, $indexes);
|
}, $indexes);
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.read', 1);
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'sum' => \count($indexes),
|
'sum' => \count($indexes),
|
||||||
'attributes' => $indexes,
|
'attributes' => $indexes,
|
||||||
|
@ -1009,7 +1240,8 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
|
||||||
->param('indexId', null, new Key(), 'Index ID.')
|
->param('indexId', null, new Key(), 'Index ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($collectionId, $indexId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $indexId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
@ -1032,6 +1264,8 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
|
||||||
'collectionId' => $collectionId,
|
'collectionId' => $collectionId,
|
||||||
])]);
|
])]);
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.read', 1);
|
||||||
|
|
||||||
$response->dynamic($index, Response::MODEL_INDEX);
|
$response->dynamic($index, Response::MODEL_INDEX);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1053,12 +1287,14 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $indexId, $response, $dbForInternal, $database, $events, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $indexId, $response, $dbForInternal, $database, $events, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $database */
|
/** @var Appwrite\Event\Event $database */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -1081,6 +1317,8 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
|
||||||
->setParam('document', $index)
|
->setParam('document', $index)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage->setParam('database.collections.update', 1);
|
||||||
|
|
||||||
$events
|
$events
|
||||||
->setParam('payload', $response->output($index, Response::MODEL_INDEX))
|
->setParam('payload', $response->output($index, Response::MODEL_INDEX))
|
||||||
;
|
;
|
||||||
|
@ -1116,12 +1354,14 @@ App::post('/v1/database/collections/:collectionId/documents')
|
||||||
->inject('dbForExternal')
|
->inject('dbForExternal')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Database\Database $dbForExternal */
|
/** @var Utopia\Database\Database $dbForExternal */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||||
|
|
||||||
|
@ -1169,6 +1409,11 @@ App::post('/v1/database/collections/:collectionId/documents')
|
||||||
throw new Exception('Document already exists', 409);
|
throw new Exception('Document already exists', 409);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('database.documents.create', 1)
|
||||||
|
->setParam('collectionId', $collectionId)
|
||||||
|
;
|
||||||
|
|
||||||
$audits
|
$audits
|
||||||
->setParam('event', 'database.documents.create')
|
->setParam('event', 'database.documents.create')
|
||||||
->setParam('resource', 'document/'.$document->getId())
|
->setParam('resource', 'document/'.$document->getId())
|
||||||
|
@ -1200,10 +1445,12 @@ App::get('/v1/database/collections/:collectionId/documents')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('dbForExternal')
|
->inject('dbForExternal')
|
||||||
->action(function ($collectionId, $queries, $limit, $offset, $after, $orderAttributes, $orderTypes, $response, $dbForInternal, $dbForExternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $queries, $limit, $offset, $after, $orderAttributes, $orderTypes, $response, $dbForInternal, $dbForExternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Database\Database $dbForExternal */
|
/** @var Utopia\Database\Database $dbForExternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -1247,6 +1494,11 @@ App::get('/v1/database/collections/:collectionId/documents')
|
||||||
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null);
|
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('database.documents.read', 1)
|
||||||
|
->setParam('collectionId', $collectionId)
|
||||||
|
;
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
|
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
|
||||||
'documents' => $documents,
|
'documents' => $documents,
|
||||||
|
@ -1269,7 +1521,8 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('dbForExternal')
|
->inject('dbForExternal')
|
||||||
->action(function ($collectionId, $documentId, $response, $dbForInternal, $dbForExternal) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $documentId, $response, $dbForInternal, $dbForExternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $$dbForInternal */
|
/** @var Utopia\Database\Database $$dbForInternal */
|
||||||
/** @var Utopia\Database\Database $dbForExternal */
|
/** @var Utopia\Database\Database $dbForExternal */
|
||||||
|
@ -1301,6 +1554,11 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
throw new Exception('No document found', 404);
|
throw new Exception('No document found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('database.documents.read', 1)
|
||||||
|
->setParam('collectionId', $collectionId)
|
||||||
|
;
|
||||||
|
|
||||||
$response->dynamic($document, Response::MODEL_DOCUMENT);
|
$response->dynamic($document, Response::MODEL_DOCUMENT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1325,11 +1583,13 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('dbForExternal')
|
->inject('dbForExternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Database\Database $dbForExternal */
|
/** @var Utopia\Database\Database $dbForExternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -1388,6 +1648,11 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
throw new Exception($exception->getMessage(), 400);
|
throw new Exception($exception->getMessage(), 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('database.documents.update', 1)
|
||||||
|
->setParam('collectionId', $collectionId)
|
||||||
|
;
|
||||||
|
|
||||||
$audits
|
$audits
|
||||||
->setParam('event', 'database.documents.update')
|
->setParam('event', 'database.documents.update')
|
||||||
->setParam('resource', 'document/'.$document->getId())
|
->setParam('resource', 'document/'.$document->getId())
|
||||||
|
@ -1415,11 +1680,13 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
->inject('dbForExternal')
|
->inject('dbForExternal')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($collectionId, $documentId, $response, $dbForInternal, $dbForExternal, $events, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($collectionId, $documentId, $response, $dbForInternal, $dbForExternal, $events, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForExternal */
|
/** @var Utopia\Database\Database $dbForExternal */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||||
|
|
||||||
|
@ -1450,6 +1717,11 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
|
|
||||||
$dbForExternal->deleteDocument($collectionId, $documentId);
|
$dbForExternal->deleteDocument($collectionId, $documentId);
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('database.documents.delete', 1)
|
||||||
|
->setParam('collectionId', $collectionId)
|
||||||
|
;
|
||||||
|
|
||||||
$events
|
$events
|
||||||
->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
|
->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
|
||||||
;
|
;
|
||||||
|
|
|
@ -146,6 +146,9 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
->label('sdk.namespace', 'functions')
|
->label('sdk.namespace', 'functions')
|
||||||
->label('sdk.method', 'getUsage')
|
->label('sdk.method', 'getUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS)
|
||||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
|
@ -164,99 +167,62 @@ App::get('/v1/functions/:functionId/usage')
|
||||||
throw new Exception('Function not found', 404);
|
throw new Exception('Function not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
$period = [
|
$period = [
|
||||||
'24h' => [
|
'24h' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
|
'period' => '30m',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
|
'limit' => 48,
|
||||||
'group' => '30m',
|
|
||||||
],
|
],
|
||||||
'7d' => [
|
'7d' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
|
'period' => '1d',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('now')),
|
'limit' => 7,
|
||||||
'group' => '1d',
|
|
||||||
],
|
],
|
||||||
'30d' => [
|
'30d' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
|
'period' => '1d',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('now')),
|
'limit' => 30,
|
||||||
'group' => '1d',
|
|
||||||
],
|
],
|
||||||
'90d' => [
|
'90d' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
|
'period' => '1d',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('now')),
|
'limit' => 90,
|
||||||
'group' => '1d',
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$client = $register->get('influxdb');
|
$metrics = [
|
||||||
|
"functions.$functionId.executions",
|
||||||
|
"functions.$functionId.failures",
|
||||||
|
"functions.$functionId.compute"
|
||||||
|
];
|
||||||
|
|
||||||
$executions = [];
|
$stats = [];
|
||||||
$failures = [];
|
|
||||||
$compute = [];
|
|
||||||
|
|
||||||
if ($client) {
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
$start = $period[$range]['start']->format(DateTime::RFC3339);
|
foreach ($metrics as $metric) {
|
||||||
$end = $period[$range]['end']->format(DateTime::RFC3339);
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
$database = $client->selectDB('telegraf');
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
// Executions
|
$stats[$metric] = [];
|
||||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
foreach ($requestDocs as $requestDoc) {
|
||||||
$points = $result->getPoints();
|
$stats[$metric][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
foreach ($points as $point) {
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
$executions[] = [
|
|
||||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
|
||||||
'date' => \strtotime($point['time']),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
// Failures
|
|
||||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' AND "functionStatus"=\'failed\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
|
||||||
$points = $result->getPoints();
|
|
||||||
|
|
||||||
foreach ($points as $point) {
|
|
||||||
$failures[] = [
|
|
||||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
|
||||||
'date' => \strtotime($point['time']),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Compute
|
$usage = new Document([
|
||||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_time" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
|
||||||
$points = $result->getPoints();
|
|
||||||
|
|
||||||
foreach ($points as $point) {
|
|
||||||
$compute[] = [
|
|
||||||
'value' => round((!empty($point['value'])) ? $point['value'] / 1000 : 0, 2), // minutes
|
|
||||||
'date' => \strtotime($point['time']),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$response->json([
|
|
||||||
'range' => $range,
|
'range' => $range,
|
||||||
'executions' => [
|
'functions.executions' => $stats["functions.$functionId.executions"],
|
||||||
'data' => $executions,
|
'functions.failures' => $stats["functions.$functionId.failures"],
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
'functions.compute' => $stats["functions.$functionId.compute"]
|
||||||
return $item['value'];
|
|
||||||
}, $executions)),
|
|
||||||
],
|
|
||||||
'failures' => [
|
|
||||||
'data' => $failures,
|
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
|
||||||
return $item['value'];
|
|
||||||
}, $failures)),
|
|
||||||
],
|
|
||||||
'compute' => [
|
|
||||||
'data' => $compute,
|
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
|
||||||
return $item['value'];
|
|
||||||
}, $compute)),
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
} else {
|
|
||||||
$response->json([]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_FUNCTIONS);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::put('/v1/functions/:functionId')
|
App::put('/v1/functions/:functionId')
|
||||||
|
|
|
@ -6,10 +6,14 @@ use Appwrite\Network\Validator\CNAME;
|
||||||
use Appwrite\Network\Validator\Domain as DomainValidator;
|
use Appwrite\Network\Validator\Domain as DomainValidator;
|
||||||
use Appwrite\Network\Validator\URL;
|
use Appwrite\Network\Validator\URL;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
|
use Utopia\Abuse\Adapters\TimeLimit;
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
|
use Utopia\Audit\Audit;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
use Utopia\Exception;
|
use Utopia\Exception;
|
||||||
|
@ -19,13 +23,11 @@ use Utopia\Validator\Integer;
|
||||||
use Utopia\Validator\Range;
|
use Utopia\Validator\Range;
|
||||||
use Utopia\Validator\Text;
|
use Utopia\Validator\Text;
|
||||||
use Utopia\Validator\WhiteList;
|
use Utopia\Validator\WhiteList;
|
||||||
use Utopia\Audit\Audit;
|
|
||||||
use Utopia\Abuse\Adapters\TimeLimit;
|
|
||||||
|
|
||||||
App::init(function ($project) {
|
App::init(function ($project) {
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
|
|
||||||
if($project->getId() !== 'console') {
|
if ($project->getId() !== 'console') {
|
||||||
throw new Exception('Access to this API is forbidden.', 401);
|
throw new Exception('Access to this API is forbidden.', 401);
|
||||||
}
|
}
|
||||||
}, ['project'], 'projects');
|
}, ['project'], 'projects');
|
||||||
|
@ -215,22 +217,25 @@ App::get('/v1/projects/:projectId')
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/projects/:projectId/usage')
|
App::get('/v1/projects/:projectId/usage')
|
||||||
->desc('Get Project')
|
->desc('Get usage stats for a project')
|
||||||
->groups(['api', 'projects'])
|
->groups(['api', 'projects'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
->label('sdk.namespace', 'projects')
|
->label('sdk.namespace', 'projects')
|
||||||
->label('sdk.method', 'getUsage')
|
->label('sdk.method', 'getUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_PROJECT)
|
||||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->inject('projectDB')
|
->inject('dbForInternal')
|
||||||
->inject('register')
|
->inject('register')
|
||||||
->action(function ($projectId, $range, $response, $dbForConsole, $projectDB, $register) {
|
->action(function ($projectId, $range, $response, $dbForConsole, $dbForInternal, $register) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForConsole */
|
/** @var Utopia\Database\Database $dbForConsole */
|
||||||
/** @var Appwrite\Database\Database $projectDB */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Registry\Registry $register */
|
/** @var Utopia\Registry\Registry $register */
|
||||||
|
|
||||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||||
|
@ -239,172 +244,72 @@ App::get('/v1/projects/:projectId/usage')
|
||||||
throw new Exception('Project not found', 404);
|
throw new Exception('Project not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
|
|
||||||
$period = [
|
$period = [
|
||||||
'24h' => [
|
'24h' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
|
'period' => '30m',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
|
'limit' => 48,
|
||||||
'group' => '30m',
|
|
||||||
],
|
],
|
||||||
'7d' => [
|
'7d' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
|
'period' => '1d',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('now')),
|
'limit' => 7,
|
||||||
'group' => '1d',
|
|
||||||
],
|
],
|
||||||
'30d' => [
|
'30d' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
|
'period' => '1d',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('now')),
|
'limit' => 30,
|
||||||
'group' => '1d',
|
|
||||||
],
|
],
|
||||||
'90d' => [
|
'90d' => [
|
||||||
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
|
'period' => '1d',
|
||||||
'end' => DateTime::createFromFormat('U', \strtotime('now')),
|
'limit' => 90,
|
||||||
'group' => '1d',
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$client = $register->get('influxdb');
|
$dbForInternal->setNamespace('project_' . $projectId . '_internal');
|
||||||
|
|
||||||
$requests = [];
|
$metrics = [
|
||||||
$network = [];
|
'requests',
|
||||||
$functions = [];
|
'network',
|
||||||
|
'executions',
|
||||||
|
'users.count',
|
||||||
|
'database.documents.count',
|
||||||
|
'database.collections.count',
|
||||||
|
'storage.total'
|
||||||
|
];
|
||||||
|
|
||||||
if ($client) {
|
$stats = [];
|
||||||
$start = $period[$range]['start']->format(DateTime::RFC3339);
|
|
||||||
$end = $period[$range]['end']->format(DateTime::RFC3339);
|
|
||||||
$database = $client->selectDB('telegraf');
|
|
||||||
|
|
||||||
// Requests
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)');
|
foreach ($metrics as $metric) {
|
||||||
$points = $result->getPoints();
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
foreach ($points as $point) {
|
$stats[$metric] = [];
|
||||||
$requests[] = [
|
foreach ($requestDocs as $requestDoc) {
|
||||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
$stats[$metric][] = [
|
||||||
'date' => \strtotime($point['time']),
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
// Network
|
|
||||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)');
|
|
||||||
$points = $result->getPoints();
|
|
||||||
|
|
||||||
foreach ($points as $point) {
|
|
||||||
$network[] = [
|
|
||||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
|
||||||
'date' => \strtotime($point['time']),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Functions
|
$usage = new Document([
|
||||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)');
|
|
||||||
$points = $result->getPoints();
|
|
||||||
|
|
||||||
foreach ($points as $point) {
|
|
||||||
$functions[] = [
|
|
||||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
|
||||||
'date' => \strtotime($point['time']),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$requests = [];
|
|
||||||
$network = [];
|
|
||||||
$functions = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Users
|
|
||||||
|
|
||||||
$projectDB->getCollection([
|
|
||||||
'limit' => 0,
|
|
||||||
'offset' => 0,
|
|
||||||
'filters' => [
|
|
||||||
'$collection=users',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$usersTotal = $projectDB->getSum();
|
|
||||||
|
|
||||||
// Documents
|
|
||||||
|
|
||||||
$collections = $projectDB->getCollection([
|
|
||||||
'limit' => 100,
|
|
||||||
'offset' => 0,
|
|
||||||
'filters' => [
|
|
||||||
'$collection=collections',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$collectionsTotal = $projectDB->getSum();
|
|
||||||
|
|
||||||
$documents = [];
|
|
||||||
|
|
||||||
foreach ($collections as $collection) {
|
|
||||||
$result = $projectDB->getCollection([
|
|
||||||
'limit' => 0,
|
|
||||||
'offset' => 0,
|
|
||||||
'filters' => [
|
|
||||||
'$collection=' . $collection['$id'],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$documents[] = ['name' => $collection['name'], 'total' => $projectDB->getSum()];
|
|
||||||
}
|
|
||||||
|
|
||||||
$response->json([
|
|
||||||
'range' => $range,
|
'range' => $range,
|
||||||
'requests' => [
|
'requests' => $stats['requests'],
|
||||||
'data' => $requests,
|
'network' => $stats['network'],
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
'functions' => $stats['executions'],
|
||||||
return $item['value'];
|
'documents' => $stats['database.documents.count'],
|
||||||
}, $requests)),
|
'collections' => $stats['database.collections.count'],
|
||||||
],
|
'users' => $stats['users.count'],
|
||||||
'network' => [
|
'storage' => $stats['storage.total']
|
||||||
'data' => \array_map(function ($value) {return ['value' => \round($value['value'] / 1000000, 2), 'date' => $value['date']];}, $network), // convert bytes to mb
|
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
|
||||||
return $item['value'];
|
|
||||||
}, $network)),
|
|
||||||
],
|
|
||||||
'functions' => [
|
|
||||||
'data' => $functions,
|
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
|
||||||
return $item['value'];
|
|
||||||
}, $functions)),
|
|
||||||
],
|
|
||||||
'collections' => [
|
|
||||||
'data' => $collections,
|
|
||||||
'total' => $collectionsTotal,
|
|
||||||
],
|
|
||||||
'documents' => [
|
|
||||||
'data' => $documents,
|
|
||||||
'total' => \array_sum(\array_map(function ($item) {
|
|
||||||
return $item['total'];
|
|
||||||
}, $documents)),
|
|
||||||
],
|
|
||||||
'users' => [
|
|
||||||
'data' => [],
|
|
||||||
'total' => $usersTotal,
|
|
||||||
],
|
|
||||||
'storage' => [
|
|
||||||
'total' => $projectDB->getCount(
|
|
||||||
[
|
|
||||||
'attribute' => 'sizeOriginal',
|
|
||||||
'filters' => [
|
|
||||||
'$collection=files',
|
|
||||||
],
|
|
||||||
]
|
|
||||||
) +
|
|
||||||
$projectDB->getCount(
|
|
||||||
[
|
|
||||||
'attribute' => 'size',
|
|
||||||
'filters' => [
|
|
||||||
'$collection=tags',
|
|
||||||
],
|
|
||||||
]
|
|
||||||
),
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_PROJECT);
|
||||||
});
|
});
|
||||||
|
|
||||||
App::patch('/v1/projects/:projectId')
|
App::patch('/v1/projects/:projectId')
|
||||||
|
@ -467,7 +372,7 @@ App::patch('/v1/projects/:projectId/service')
|
||||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
->label('sdk.response.model', Response::MODEL_PROJECT)
|
->label('sdk.response.model', Response::MODEL_PROJECT)
|
||||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||||
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), function($element) {return $element['optional'];})), true), 'Service name.')
|
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), function ($element) {return $element['optional'];})), true), 'Service name.')
|
||||||
->param('status', null, new Boolean(), 'Service status.')
|
->param('status', null, new Boolean(), 'Service status.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Utopia\Validator\HexColor;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\Cache\Adapter\Filesystem;
|
use Utopia\Cache\Adapter\Filesystem;
|
||||||
use Appwrite\ClamAV\Network;
|
use Appwrite\ClamAV\Network;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Appwrite\Database\Validator\CustomId;
|
use Appwrite\Database\Validator\CustomId;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
|
@ -22,6 +23,7 @@ use Utopia\Image\Image;
|
||||||
use Appwrite\OpenSSL\OpenSSL;
|
use Appwrite\OpenSSL\OpenSSL;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
|
|
||||||
App::post('/v1/storage/files')
|
App::post('/v1/storage/files')
|
||||||
|
@ -54,7 +56,7 @@ App::post('/v1/storage/files')
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Database\Document $user */
|
/** @var Utopia\Database\Document $user */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $usage */
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$file = $request->getFiles('file');
|
$file = $request->getFiles('file');
|
||||||
|
|
||||||
|
@ -150,6 +152,8 @@ App::post('/v1/storage/files')
|
||||||
|
|
||||||
$usage
|
$usage
|
||||||
->setParam('storage', $sizeActual)
|
->setParam('storage', $sizeActual)
|
||||||
|
->setParam('storage.files.create', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
;
|
;
|
||||||
|
|
||||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||||
|
@ -175,9 +179,11 @@ App::get('/v1/storage/files')
|
||||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
|
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
|
||||||
|
|
||||||
|
@ -189,6 +195,11 @@ App::get('/v1/storage/files')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('storage.files.read', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
|
;
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $afterFile ?? null),
|
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $afterFile ?? null),
|
||||||
'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT),
|
'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT),
|
||||||
|
@ -209,16 +220,21 @@ App::get('/v1/storage/files/:fileId')
|
||||||
->param('fileId', '', new UID(), 'File unique ID.')
|
->param('fileId', '', new UID(), 'File unique ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($fileId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($fileId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$file = $dbForInternal->getDocument('files', $fileId);
|
$file = $dbForInternal->getDocument('files', $fileId);
|
||||||
|
|
||||||
if (empty($file->getId())) {
|
if (empty($file->getId())) {
|
||||||
throw new Exception('File not found', 404);
|
throw new Exception('File not found', 404);
|
||||||
}
|
}
|
||||||
|
$usage
|
||||||
|
->setParam('storage.files.read', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
|
;
|
||||||
$response->dynamic($file, Response::MODEL_FILE);
|
$response->dynamic($file, Response::MODEL_FILE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -249,11 +265,13 @@ App::get('/v1/storage/files/:fileId/preview')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal, $usage) {
|
||||||
/** @var Utopia\Swoole\Request $request */
|
/** @var Utopia\Swoole\Request $request */
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $stats */
|
||||||
|
|
||||||
$storage = 'files';
|
$storage = 'files';
|
||||||
|
|
||||||
|
@ -366,6 +384,11 @@ App::get('/v1/storage/files/:fileId/preview')
|
||||||
|
|
||||||
$cache->save($key, $data);
|
$cache->save($key, $data);
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('storage.files.read', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
|
;
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->setContentType($outputs[$output])
|
->setContentType($outputs[$output])
|
||||||
->addHeader('Expires', $date)
|
->addHeader('Expires', $date)
|
||||||
|
@ -390,9 +413,11 @@ App::get('/v1/storage/files/:fileId/download')
|
||||||
->param('fileId', '', new UID(), 'File unique ID.')
|
->param('fileId', '', new UID(), 'File unique ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($fileId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($fileId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$file = $dbForInternal->getDocument('files', $fileId);
|
$file = $dbForInternal->getDocument('files', $fileId);
|
||||||
|
|
||||||
|
@ -424,6 +449,11 @@ App::get('/v1/storage/files/:fileId/download')
|
||||||
|
|
||||||
$source = $compressor->decompress($source);
|
$source = $compressor->decompress($source);
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('storage.files.read', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
|
;
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
$response
|
$response
|
||||||
->setContentType($file->getAttribute('mimeType'))
|
->setContentType($file->getAttribute('mimeType'))
|
||||||
|
@ -448,9 +478,11 @@ App::get('/v1/storage/files/:fileId/view')
|
||||||
->param('fileId', '', new UID(), 'File unique ID.')
|
->param('fileId', '', new UID(), 'File unique ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($fileId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($fileId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$file = $dbForInternal->getDocument('files', $fileId);
|
$file = $dbForInternal->getDocument('files', $fileId);
|
||||||
$mimes = Config::getParam('storage-mimes');
|
$mimes = Config::getParam('storage-mimes');
|
||||||
|
@ -490,6 +522,11 @@ App::get('/v1/storage/files/:fileId/view')
|
||||||
$output = $compressor->decompress($source);
|
$output = $compressor->decompress($source);
|
||||||
$fileName = $file->getAttribute('name', '');
|
$fileName = $file->getAttribute('name', '');
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('storage.files.read', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
|
;
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
$response
|
$response
|
||||||
->setContentType($contentType)
|
->setContentType($contentType)
|
||||||
|
@ -520,7 +557,8 @@ App::put('/v1/storage/files/:fileId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('audits')
|
->inject('audits')
|
||||||
->action(function ($fileId, $read, $write, $response, $dbForInternal, $audits) {
|
->inject('usage')
|
||||||
|
->action(function ($fileId, $read, $write, $response, $dbForInternal, $audits, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
|
@ -542,6 +580,11 @@ App::put('/v1/storage/files/:fileId')
|
||||||
->setParam('resource', 'file/'.$file->getId())
|
->setParam('resource', 'file/'.$file->getId())
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('storage.files.update', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
|
;
|
||||||
|
|
||||||
$response->dynamic($file, Response::MODEL_FILE);
|
$response->dynamic($file, Response::MODEL_FILE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -567,7 +610,7 @@ App::delete('/v1/storage/files/:fileId')
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $audits */
|
/** @var Appwrite\Event\Event $audits */
|
||||||
/** @var Appwrite\Event\Event $usage */
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$file = $dbForInternal->getDocument('files', $fileId);
|
$file = $dbForInternal->getDocument('files', $fileId);
|
||||||
|
|
||||||
|
@ -590,6 +633,8 @@ App::delete('/v1/storage/files/:fileId')
|
||||||
|
|
||||||
$usage
|
$usage
|
||||||
->setParam('storage', $file->getAttribute('size', 0) * -1)
|
->setParam('storage', $file->getAttribute('size', 0) * -1)
|
||||||
|
->setParam('storage.files.delete', 1)
|
||||||
|
->setParam('bucketId', 'default')
|
||||||
;
|
;
|
||||||
|
|
||||||
$events
|
$events
|
||||||
|
@ -598,3 +643,158 @@ App::delete('/v1/storage/files/:fileId')
|
||||||
|
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::get('/v1/storage/usage')
|
||||||
|
->desc('Get usage stats for storage')
|
||||||
|
->groups(['api', 'storage'])
|
||||||
|
->label('scope', 'files.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'storage')
|
||||||
|
->label('sdk.method', 'getUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_STORAGE)
|
||||||
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForInternal')
|
||||||
|
->action(function ($range, $response, $dbForInternal) {
|
||||||
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
|
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
|
$period = [
|
||||||
|
'24h' => [
|
||||||
|
'period' => '30m',
|
||||||
|
'limit' => 48,
|
||||||
|
],
|
||||||
|
'7d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 7,
|
||||||
|
],
|
||||||
|
'30d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 30,
|
||||||
|
],
|
||||||
|
'90d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 90,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$metrics = [
|
||||||
|
"storage.total",
|
||||||
|
"storage.files.count"
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [];
|
||||||
|
|
||||||
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
|
foreach ($metrics as $metric) {
|
||||||
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
|
$stats[$metric] = [];
|
||||||
|
foreach ($requestDocs as $requestDoc) {
|
||||||
|
$stats[$metric][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$usage = new Document([
|
||||||
|
'range' => $range,
|
||||||
|
'storage' => $stats['storage.total'],
|
||||||
|
'files' => $stats['storage.files.count']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_STORAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
App::get('/v1/storage/:bucketId/usage')
|
||||||
|
->desc('Get usage stats for a storage bucket')
|
||||||
|
->groups(['api', 'storage'])
|
||||||
|
->label('scope', 'files.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'storage')
|
||||||
|
->label('sdk.method', 'getBucketUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_BUCKETS)
|
||||||
|
->param('bucketId', '', new UID(), 'Bucket unique ID.')
|
||||||
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForInternal')
|
||||||
|
->action(function ($bucketId, $range, $response, $dbForInternal) {
|
||||||
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
// TODO: Check if the storage bucket exists else throw 404
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
|
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
|
$period = [
|
||||||
|
'24h' => [
|
||||||
|
'period' => '30m',
|
||||||
|
'limit' => 48,
|
||||||
|
],
|
||||||
|
'7d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 7,
|
||||||
|
],
|
||||||
|
'30d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 30,
|
||||||
|
],
|
||||||
|
'90d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 90,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$metrics = [
|
||||||
|
"storage.buckets.$bucketId.files.count",
|
||||||
|
"storage.buckets.$bucketId.files.create",
|
||||||
|
"storage.buckets.$bucketId.files.read",
|
||||||
|
"storage.buckets.$bucketId.files.update",
|
||||||
|
"storage.buckets.$bucketId.files.delete"
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [];
|
||||||
|
|
||||||
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
|
foreach ($metrics as $metric) {
|
||||||
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
|
$stats[$metric] = [];
|
||||||
|
foreach ($requestDocs as $requestDoc) {
|
||||||
|
$stats[$metric][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$usage = new Document([
|
||||||
|
'range' => $range,
|
||||||
|
'files.count' => $stats["storage.buckets.$bucketId.files.count"],
|
||||||
|
'files.create' => $stats["storage.buckets.$bucketId.files.create"],
|
||||||
|
'files.read' => $stats["storage.buckets.$bucketId.files.read"],
|
||||||
|
'files.update' => $stats["storage.buckets.$bucketId.files.update"],
|
||||||
|
'files.delete' => $stats["storage.buckets.$bucketId.files.delete"]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_BUCKETS);
|
||||||
|
});
|
|
@ -17,7 +17,10 @@ use Utopia\Database\Exception\Duplicate;
|
||||||
use Utopia\Database\Validator\UID;
|
use Utopia\Database\Validator\UID;
|
||||||
use DeviceDetector\DeviceDetector;
|
use DeviceDetector\DeviceDetector;
|
||||||
use Appwrite\Database\Validator\CustomId;
|
use Appwrite\Database\Validator\CustomId;
|
||||||
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
|
||||||
App::post('/v1/users')
|
App::post('/v1/users')
|
||||||
->desc('Create User')
|
->desc('Create User')
|
||||||
|
@ -37,9 +40,11 @@ App::post('/v1/users')
|
||||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($userId, $email, $password, $name, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $email, $password, $name, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$email = \strtolower($email);
|
$email = \strtolower($email);
|
||||||
|
|
||||||
|
@ -66,6 +71,10 @@ App::post('/v1/users')
|
||||||
throw new Exception('Account already exists', 409);
|
throw new Exception('Account already exists', 409);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.create', 1)
|
||||||
|
;
|
||||||
|
|
||||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
@ -88,9 +97,11 @@ App::get('/v1/users')
|
||||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
if (!empty($after)) {
|
if (!empty($after)) {
|
||||||
$afterUser = $dbForInternal->getDocument('users', $after);
|
$afterUser = $dbForInternal->getDocument('users', $after);
|
||||||
|
@ -103,6 +114,10 @@ App::get('/v1/users')
|
||||||
$results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $afterUser ?? null);
|
$results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $afterUser ?? null);
|
||||||
$sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT);
|
$sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'users' => $results,
|
'users' => $results,
|
||||||
'sum' => $sum,
|
'sum' => $sum,
|
||||||
|
@ -123,9 +138,11 @@ App::get('/v1/users/:userId')
|
||||||
->param('userId', '', new UID(), 'User unique ID.')
|
->param('userId', '', new UID(), 'User unique ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($userId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -133,6 +150,9 @@ App::get('/v1/users/:userId')
|
||||||
throw new Exception('User not found', 404);
|
throw new Exception('User not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -150,9 +170,11 @@ App::get('/v1/users/:userId/prefs')
|
||||||
->param('userId', '', new UID(), 'User unique ID.')
|
->param('userId', '', new UID(), 'User unique ID.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($userId, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -162,6 +184,9 @@ App::get('/v1/users/:userId/prefs')
|
||||||
|
|
||||||
$prefs = $user->getAttribute('prefs', new \stdClass());
|
$prefs = $user->getAttribute('prefs', new \stdClass());
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -180,10 +205,12 @@ App::get('/v1/users/:userId/sessions')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->action(function ($userId, $response, $dbForInternal, $locale) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $response, $dbForInternal, $locale, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -203,6 +230,9 @@ App::get('/v1/users/:userId/sessions')
|
||||||
$sessions[$key] = $session;
|
$sessions[$key] = $session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'sessions' => $sessions,
|
'sessions' => $sessions,
|
||||||
'sum' => count($sessions),
|
'sum' => count($sessions),
|
||||||
|
@ -225,12 +255,14 @@ App::get('/v1/users/:userId/logs')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->action(function ($userId, $response, $dbForInternal, $locale, $geodb) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $response, $dbForInternal, $locale, $geodb, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Document $project */
|
/** @var Utopia\Database\Document $project */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Utopia\Locale\Locale $locale */
|
/** @var Utopia\Locale\Locale $locale */
|
||||||
/** @var MaxMind\Db\Reader $geodb */
|
/** @var MaxMind\Db\Reader $geodb */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -312,6 +344,9 @@ App::get('/v1/users/:userId/logs')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.read', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -331,9 +366,11 @@ App::patch('/v1/users/:userId/status')
|
||||||
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`')
|
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($userId, $status, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $status, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -343,6 +380,9 @@ App::patch('/v1/users/:userId/status')
|
||||||
|
|
||||||
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status));
|
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status));
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -362,9 +402,11 @@ App::patch('/v1/users/:userId/verification')
|
||||||
->param('emailVerification', false, new Boolean(), 'User Email Verification Status.')
|
->param('emailVerification', false, new Boolean(), 'User Email Verification Status.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($userId, $emailVerification, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $emailVerification, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -374,6 +416,9 @@ App::patch('/v1/users/:userId/verification')
|
||||||
|
|
||||||
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification));
|
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification));
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic($user, Response::MODEL_USER);
|
$response->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -516,9 +561,11 @@ App::patch('/v1/users/:userId/prefs')
|
||||||
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
|
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->action(function ($userId, $prefs, $response, $dbForInternal) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $prefs, $response, $dbForInternal, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -528,6 +575,9 @@ App::patch('/v1/users/:userId/prefs')
|
||||||
|
|
||||||
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
;
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -547,10 +597,12 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($userId, $sessionId, $response, $dbForInternal, $events) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $sessionId, $response, $dbForInternal, $events, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -577,6 +629,11 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
->setParam('users.sessions.delete', 1)
|
||||||
|
;
|
||||||
|
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -595,10 +652,12 @@ App::delete('/v1/users/:userId/sessions')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function ($userId, $response, $dbForInternal, $events) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $response, $dbForInternal, $events, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -618,6 +677,10 @@ App::delete('/v1/users/:userId/sessions')
|
||||||
->setParam('eventData', $response->output($user, Response::MODEL_USER))
|
->setParam('eventData', $response->output($user, Response::MODEL_USER))
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.update', 1)
|
||||||
|
->setParam('users.sessions.delete', 1)
|
||||||
|
;
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -637,11 +700,13 @@ App::delete('/v1/users/:userId')
|
||||||
->inject('dbForInternal')
|
->inject('dbForInternal')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->inject('deletes')
|
->inject('deletes')
|
||||||
->action(function ($userId, $response, $dbForInternal, $events, $deletes) {
|
->inject('usage')
|
||||||
|
->action(function ($userId, $response, $dbForInternal, $events, $deletes, $usage) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Utopia\Database\Database $dbForInternal */
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
/** @var Appwrite\Event\Event $events */
|
/** @var Appwrite\Event\Event $events */
|
||||||
/** @var Appwrite\Event\Event $deletes */
|
/** @var Appwrite\Event\Event $deletes */
|
||||||
|
/** @var Appwrite\Stats\Stats $usage */
|
||||||
|
|
||||||
$user = $dbForInternal->getDocument('users', $userId);
|
$user = $dbForInternal->getDocument('users', $userId);
|
||||||
|
|
||||||
|
@ -653,11 +718,6 @@ App::delete('/v1/users/:userId')
|
||||||
throw new Exception('Failed to remove user from DB', 500);
|
throw new Exception('Failed to remove user from DB', 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// $dbForInternal->createDocument('users', new Document([
|
|
||||||
// '$id' => $userId,
|
|
||||||
// '$read' => ['role:all'],
|
|
||||||
// ]));
|
|
||||||
|
|
||||||
$deletes
|
$deletes
|
||||||
->setParam('type', DELETE_TYPE_DOCUMENT)
|
->setParam('type', DELETE_TYPE_DOCUMENT)
|
||||||
->setParam('document', $user)
|
->setParam('document', $user)
|
||||||
|
@ -667,5 +727,96 @@ App::delete('/v1/users/:userId')
|
||||||
->setParam('eventData', $response->output($user, Response::MODEL_USER))
|
->setParam('eventData', $response->output($user, Response::MODEL_USER))
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$usage
|
||||||
|
->setParam('users.delete', 1)
|
||||||
|
;
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::get('/v1/users/usage')
|
||||||
|
->desc('Get usage stats for the users API')
|
||||||
|
->groups(['api', 'users'])
|
||||||
|
->label('scope', 'users.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->label('sdk.method', 'getUsage')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
|
||||||
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
|
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(function($value) { return "oauth-".$value; }, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForInternal')
|
||||||
|
->inject('register')
|
||||||
|
->action(function ($range, $provider, $response, $dbForInternal) {
|
||||||
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
|
/** @var Utopia\Database\Database $dbForInternal */
|
||||||
|
|
||||||
|
$usage = [];
|
||||||
|
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||||
|
$period = [
|
||||||
|
'24h' => [
|
||||||
|
'period' => '30m',
|
||||||
|
'limit' => 48,
|
||||||
|
],
|
||||||
|
'7d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 7,
|
||||||
|
],
|
||||||
|
'30d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 30,
|
||||||
|
],
|
||||||
|
'90d' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 90,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$metrics = [
|
||||||
|
"users.count",
|
||||||
|
"users.create",
|
||||||
|
"users.read",
|
||||||
|
"users.update",
|
||||||
|
"users.delete",
|
||||||
|
"users.sessions.create",
|
||||||
|
"users.sessions.$provider.create",
|
||||||
|
"users.sessions.delete"
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [];
|
||||||
|
|
||||||
|
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||||
|
foreach ($metrics as $metric) {
|
||||||
|
$requestDocs = $dbForInternal->find('stats', [
|
||||||
|
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||||
|
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||||
|
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||||
|
|
||||||
|
$stats[$metric] = [];
|
||||||
|
foreach ($requestDocs as $requestDoc) {
|
||||||
|
$stats[$metric][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$stats[$metric] = array_reverse($stats[$metric]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$usage = new Document([
|
||||||
|
'range' => $range,
|
||||||
|
'users.count' => $stats["users.count"],
|
||||||
|
'users.create' => $stats["users.create"],
|
||||||
|
'users.read' => $stats["users.read"],
|
||||||
|
'users.update' => $stats["users.update"],
|
||||||
|
'users.delete' => $stats["users.delete"],
|
||||||
|
'sessions.create' => $stats["users.sessions.create"],
|
||||||
|
'sessions.provider.create' => $stats["users.sessions.$provider.create"],
|
||||||
|
'sessions.delete' => $stats["users.sessions.delete"]
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($usage, Response::MODEL_USAGE_USERS);
|
||||||
|
});
|
|
@ -104,6 +104,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
||||||
->setParam('httpRequest', 1)
|
->setParam('httpRequest', 1)
|
||||||
->setParam('httpUrl', $request->getHostname().$request->getURI())
|
->setParam('httpUrl', $request->getHostname().$request->getURI())
|
||||||
->setParam('httpMethod', $request->getMethod())
|
->setParam('httpMethod', $request->getMethod())
|
||||||
|
->setParam('httpPath', $route->getPath())
|
||||||
->setParam('networkRequestSize', 0)
|
->setParam('networkRequestSize', 0)
|
||||||
->setParam('networkResponseSize', 0)
|
->setParam('networkResponseSize', 0)
|
||||||
->setParam('storage', 0)
|
->setParam('storage', 0)
|
||||||
|
|
25
app/init.php
25
app/init.php
|
@ -17,6 +17,7 @@ ini_set('display_startup_errors', 1);
|
||||||
ini_set('default_socket_timeout', -1);
|
ini_set('default_socket_timeout', -1);
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
use Appwrite\Extend\PDO;
|
||||||
use Ahc\Jwt\JWT;
|
use Ahc\Jwt\JWT;
|
||||||
use Ahc\Jwt\JWTException;
|
use Ahc\Jwt\JWTException;
|
||||||
use Appwrite\Auth\Auth;
|
use Appwrite\Auth\Auth;
|
||||||
|
@ -88,6 +89,7 @@ const DELETE_TYPE_EXECUTIONS = 'executions';
|
||||||
const DELETE_TYPE_AUDIT = 'audit';
|
const DELETE_TYPE_AUDIT = 'audit';
|
||||||
const DELETE_TYPE_ABUSE = 'abuse';
|
const DELETE_TYPE_ABUSE = 'abuse';
|
||||||
const DELETE_TYPE_CERTIFICATES = 'certificates';
|
const DELETE_TYPE_CERTIFICATES = 'certificates';
|
||||||
|
const DELETE_TYPE_USAGE = 'usage';
|
||||||
// Mail Worker Types
|
// Mail Worker Types
|
||||||
const MAIL_TYPE_VERIFICATION = 'verification';
|
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||||
const MAIL_TYPE_RECOVERY = 'recovery';
|
const MAIL_TYPE_RECOVERY = 'recovery';
|
||||||
|
@ -345,6 +347,29 @@ $register->set('smtp', function () {
|
||||||
$register->set('geodb', function () {
|
$register->set('geodb', function () {
|
||||||
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2021-06.mmdb');
|
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2021-06.mmdb');
|
||||||
});
|
});
|
||||||
|
$register->set('db', function () { // This is usually for our workers or CLI commands scope
|
||||||
|
$dbHost = App::getEnv('_APP_DB_HOST', '');
|
||||||
|
$dbUser = App::getEnv('_APP_DB_USER', '');
|
||||||
|
$dbPass = App::getEnv('_APP_DB_PASS', '');
|
||||||
|
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
|
||||||
|
|
||||||
|
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
||||||
|
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
|
||||||
|
PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||||
|
PDO::ATTR_PERSISTENT => true,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $pdo;
|
||||||
|
});
|
||||||
|
$register->set('cache', function () { // This is usually for our workers or CLI commands scope
|
||||||
|
$redis = new Redis();
|
||||||
|
$redis->pconnect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', ''));
|
||||||
|
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
|
||||||
|
|
||||||
|
return $redis;
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Localization
|
* Localization
|
||||||
|
|
|
@ -39,17 +39,29 @@ $cli
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifyDeleteUsageStats(int $interval30m, int $interval1d)
|
||||||
|
{
|
||||||
|
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
|
||||||
|
'type' => DELETE_TYPE_USAGE_STATS,
|
||||||
|
'timestamp1d' => time() - $interval1d,
|
||||||
|
'timestamp30m' => time() - $interval30m,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// # of days in seconds (1 day = 86400s)
|
// # of days in seconds (1 day = 86400s)
|
||||||
$interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400');
|
$interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400');
|
||||||
$executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600');
|
$executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600');
|
||||||
$auditLogRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', '1209600');
|
$auditLogRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', '1209600');
|
||||||
$abuseLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', '86400');
|
$abuseLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', '86400');
|
||||||
|
$usageStatsRetention30m = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_30M', '129600');//36 hours
|
||||||
|
$usageStatsRetention1d = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_1D', '8640000'); // 100 days
|
||||||
|
|
||||||
Console::loop(function() use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention){
|
Console::loop(function() use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) {
|
||||||
$time = date('d-m-Y H:i:s', time());
|
$time = date('d-m-Y H:i:s', time());
|
||||||
Console::info("[{$time}] Notifying deletes workers every {$interval} seconds");
|
Console::info("[{$time}] Notifying deletes workers every {$interval} seconds");
|
||||||
notifyDeleteExecutionLogs($executionLogsRetention);
|
notifyDeleteExecutionLogs($executionLogsRetention);
|
||||||
notifyDeleteAbuseLogs($abuseLogsRetention);
|
notifyDeleteAbuseLogs($abuseLogsRetention);
|
||||||
notifyDeleteAuditLogs($auditLogRetention);
|
notifyDeleteAuditLogs($auditLogRetention);
|
||||||
|
notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d);
|
||||||
}, $interval);
|
}, $interval);
|
||||||
});
|
});
|
586
app/tasks/usage.php
Normal file
586
app/tasks/usage.php
Normal file
|
@ -0,0 +1,586 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
global $cli, $register;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../init.php';
|
||||||
|
|
||||||
|
use Utopia\App;
|
||||||
|
use Utopia\Cache\Adapter\Redis;
|
||||||
|
use Utopia\Cache\Cache;
|
||||||
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Database\Adapter\MariaDB;
|
||||||
|
use Utopia\Database\Database;
|
||||||
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metrics We collect
|
||||||
|
*
|
||||||
|
* General
|
||||||
|
*
|
||||||
|
* requests
|
||||||
|
* network
|
||||||
|
* executions
|
||||||
|
*
|
||||||
|
* Database
|
||||||
|
*
|
||||||
|
* database.collections.create
|
||||||
|
* database.collections.read
|
||||||
|
* database.collections.update
|
||||||
|
* database.collections.delete
|
||||||
|
* database.documents.create
|
||||||
|
* database.documents.read
|
||||||
|
* database.documents.update
|
||||||
|
* database.documents.delete
|
||||||
|
* database.collections.{collectionId}.documents.create
|
||||||
|
* database.collections.{collectionId}.documents.read
|
||||||
|
* database.collections.{collectionId}.documents.update
|
||||||
|
* database.collections.{collectionId}.documents.delete
|
||||||
|
*
|
||||||
|
* Storage
|
||||||
|
*
|
||||||
|
* storage.buckets.{bucketId}.files.create
|
||||||
|
* storage.buckets.{bucketId}.files.read
|
||||||
|
* storage.buckets.{bucketId}.files.update
|
||||||
|
* storage.buckets.{bucketId}.files.delete
|
||||||
|
*
|
||||||
|
* Users
|
||||||
|
*
|
||||||
|
* users.create
|
||||||
|
* users.read
|
||||||
|
* users.update
|
||||||
|
* users.delete
|
||||||
|
* users.sessions.create
|
||||||
|
* users.sessions.{provider}.create
|
||||||
|
* users.sessions.delete
|
||||||
|
*
|
||||||
|
* Functions
|
||||||
|
*
|
||||||
|
* functions.{functionId}.executions
|
||||||
|
* functions.{functionId}.failures
|
||||||
|
* functions.{functionId}.compute
|
||||||
|
*
|
||||||
|
* Counters
|
||||||
|
*
|
||||||
|
* users.count
|
||||||
|
* storage.files.count
|
||||||
|
* database.collections.count
|
||||||
|
* database.documents.count
|
||||||
|
* database.collections.{collectionId}.documents.count
|
||||||
|
*
|
||||||
|
* Totals
|
||||||
|
*
|
||||||
|
* storage.total
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
$cli
|
||||||
|
->task('usage')
|
||||||
|
->desc('Schedules syncing data from influxdb to Appwrite console db')
|
||||||
|
->action(function () use ($register) {
|
||||||
|
Console::title('Usage Aggregation V1');
|
||||||
|
Console::success(APP_NAME . ' usage aggregation process v1 has started');
|
||||||
|
|
||||||
|
$interval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '30'); // 30 seconds (by default)
|
||||||
|
$periods = [
|
||||||
|
[
|
||||||
|
'key' => '30m',
|
||||||
|
'startTime' => '-24 hours',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => '1d',
|
||||||
|
'startTime' => '-90 days',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// all the metrics that we are collecting at the moment
|
||||||
|
$globalMetrics = [
|
||||||
|
'requests' => [
|
||||||
|
'table' => 'appwrite_usage_requests_all',
|
||||||
|
],
|
||||||
|
'network' => [
|
||||||
|
'table' => 'appwrite_usage_network_all',
|
||||||
|
],
|
||||||
|
'executions' => [
|
||||||
|
'table' => 'appwrite_usage_executions_all',
|
||||||
|
],
|
||||||
|
'database.collections.create' => [
|
||||||
|
'table' => 'appwrite_usage_database_collections_create',
|
||||||
|
],
|
||||||
|
'database.collections.read' => [
|
||||||
|
'table' => 'appwrite_usage_database_collections_read',
|
||||||
|
],
|
||||||
|
'database.collections.update' => [
|
||||||
|
'table' => 'appwrite_usage_database_collections_update',
|
||||||
|
],
|
||||||
|
'database.collections.delete' => [
|
||||||
|
'table' => 'appwrite_usage_database_collections_delete',
|
||||||
|
],
|
||||||
|
'database.documents.create' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_create',
|
||||||
|
],
|
||||||
|
'database.documents.read' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_read',
|
||||||
|
],
|
||||||
|
'database.documents.update' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_update',
|
||||||
|
],
|
||||||
|
'database.documents.delete' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_delete',
|
||||||
|
],
|
||||||
|
'database.collections.collectionId.documents.create' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_create',
|
||||||
|
'groupBy' => 'collectionId',
|
||||||
|
],
|
||||||
|
'database.collections.collectionId.documents.read' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_read',
|
||||||
|
'groupBy' => 'collectionId',
|
||||||
|
],
|
||||||
|
'database.collections.collectionId.documents.update' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_update',
|
||||||
|
'groupBy' => 'collectionId',
|
||||||
|
],
|
||||||
|
'database.collections.collectionId.documents.delete' => [
|
||||||
|
'table' => 'appwrite_usage_database_documents_delete',
|
||||||
|
'groupBy' => 'collectionId',
|
||||||
|
],
|
||||||
|
'storage.buckets.bucketId.files.create' => [
|
||||||
|
'table' => 'appwrite_usage_storage_files_create',
|
||||||
|
'groupBy' => 'bucketId',
|
||||||
|
],
|
||||||
|
'storage.buckets.bucketId.files.read' => [
|
||||||
|
'table' => 'appwrite_usage_storage_files_read',
|
||||||
|
'groupBy' => 'bucketId',
|
||||||
|
],
|
||||||
|
'storage.buckets.bucketId.files.update' => [
|
||||||
|
'table' => 'appwrite_usage_storage_files_update',
|
||||||
|
'groupBy' => 'bucketId',
|
||||||
|
],
|
||||||
|
'storage.buckets.bucketId.files.delete' => [
|
||||||
|
'table' => 'appwrite_usage_storage_files_delete',
|
||||||
|
'groupBy' => 'bucketId',
|
||||||
|
],
|
||||||
|
'users.create' => [
|
||||||
|
'table' => 'appwrite_usage_users_create',
|
||||||
|
],
|
||||||
|
'users.read' => [
|
||||||
|
'table' => 'appwrite_usage_users_read',
|
||||||
|
],
|
||||||
|
'users.update' => [
|
||||||
|
'table' => 'appwrite_usage_users_update',
|
||||||
|
],
|
||||||
|
'users.delete' => [
|
||||||
|
'table' => 'appwrite_usage_users_delete',
|
||||||
|
],
|
||||||
|
'users.sessions.create' => [
|
||||||
|
'table' => 'appwrite_usage_users_sessions_create',
|
||||||
|
],
|
||||||
|
'users.sessions.provider.create' => [
|
||||||
|
'table' => 'appwrite_usage_users_sessions_create',
|
||||||
|
'groupBy' => 'provider',
|
||||||
|
],
|
||||||
|
'users.sessions.delete' => [
|
||||||
|
'table' => 'appwrite_usage_users_sessions_delete',
|
||||||
|
],
|
||||||
|
'functions.functionId.executions' => [
|
||||||
|
'table' => 'appwrite_usage_executions_all',
|
||||||
|
'groupBy' => 'functionId',
|
||||||
|
],
|
||||||
|
'functions.functionId.compute' => [
|
||||||
|
'table' => 'appwrite_usage_executions_time',
|
||||||
|
'groupBy' => 'functionId',
|
||||||
|
],
|
||||||
|
'functions.functionId.failures' => [
|
||||||
|
'table' => 'appwrite_usage_executions_all',
|
||||||
|
'groupBy' => 'functionId',
|
||||||
|
'filters' => [
|
||||||
|
'functionStatus' => 'failed',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO Maybe move this to the setResource method, and reuse in the http.php file
|
||||||
|
$attempts = 0;
|
||||||
|
$max = 10;
|
||||||
|
$sleep = 1;
|
||||||
|
|
||||||
|
do { // connect to db
|
||||||
|
try {
|
||||||
|
$attempts++;
|
||||||
|
$db = $register->get('db');
|
||||||
|
$redis = $register->get('cache');
|
||||||
|
break; // leave the do-while if successful
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||||
|
if ($attempts >= $max) {
|
||||||
|
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
sleep($sleep);
|
||||||
|
}
|
||||||
|
} while ($attempts < $max);
|
||||||
|
|
||||||
|
// TODO use inject
|
||||||
|
$cacheAdapter = new Cache(new Redis($redis));
|
||||||
|
$dbForProject = new Database(new MariaDB($db), $cacheAdapter);
|
||||||
|
$dbForConsole = new Database(new MariaDB($db), $cacheAdapter);
|
||||||
|
$dbForConsole->setNamespace('project_console_internal');
|
||||||
|
|
||||||
|
$latestTime = [];
|
||||||
|
|
||||||
|
Authorization::disable();
|
||||||
|
|
||||||
|
$iterations = 0;
|
||||||
|
Console::loop(function () use ($interval, $register, $dbForProject, $dbForConsole, $globalMetrics, $periods, &$latestTime, &$iterations) {
|
||||||
|
$now = date('d-m-Y H:i:s', time());
|
||||||
|
Console::info("[{$now}] Aggregating usage data every {$interval} seconds");
|
||||||
|
|
||||||
|
$loopStart = microtime(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregate InfluxDB every 30 seconds
|
||||||
|
* @var InfluxDB\Client $client
|
||||||
|
*/
|
||||||
|
$client = $register->get('influxdb');
|
||||||
|
if ($client) {
|
||||||
|
$attempts = 0;
|
||||||
|
$max = 10;
|
||||||
|
$sleep = 1;
|
||||||
|
|
||||||
|
$database = $client->selectDB('telegraf');
|
||||||
|
do { // check if telegraf database is ready
|
||||||
|
$attempts++;
|
||||||
|
if(!in_array('telegraf', $client->listDatabases())) {
|
||||||
|
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||||
|
if($attempts >= $max) {
|
||||||
|
throw new \Exception('InfluxDB database not ready yet');
|
||||||
|
}
|
||||||
|
sleep($sleep);
|
||||||
|
} else {
|
||||||
|
break; // leave the do-while if successful
|
||||||
|
}
|
||||||
|
} while ($attempts < $max);
|
||||||
|
|
||||||
|
// sync data
|
||||||
|
foreach ($globalMetrics as $metric => $options) { //for each metrics
|
||||||
|
foreach ($periods as $period) { // aggregate data for each period
|
||||||
|
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
|
||||||
|
if (!empty($latestTime[$metric][$period['key']])) {
|
||||||
|
$start = DateTime::createFromFormat('U', $latestTime[$metric][$period['key']])->format(DateTime::RFC3339);
|
||||||
|
}
|
||||||
|
$end = DateTime::createFromFormat('U', \strtotime('now'))->format(DateTime::RFC3339);
|
||||||
|
|
||||||
|
$table = $options['table']; //Which influxdb table to query for this metric
|
||||||
|
$groupBy = empty($options['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(function ($filter, $value) {
|
||||||
|
return '"' . $filter . '"=\'' . $value . '\'';
|
||||||
|
}, array_keys($filters), array_values($filters)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $database->query('SELECT sum(value) AS "value" FROM "' . $table . '" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\'' . (empty($filters) ? '' : $filters) . ' GROUP BY time(' . $period['key'] . '), "projectId"' . $groupBy . ' FILL(null)');
|
||||||
|
|
||||||
|
$points = $result->getPoints();
|
||||||
|
foreach ($points as $point) {
|
||||||
|
$projectId = $point['projectId'];
|
||||||
|
|
||||||
|
if (!empty($projectId) && $projectId != 'console') {
|
||||||
|
$dbForProject->setNamespace('project_' . $projectId . '_internal');
|
||||||
|
$metricUpdated = $metric;
|
||||||
|
|
||||||
|
if (!empty($groupBy)) {
|
||||||
|
$groupedBy = $point[$options['groupBy']] ?? '';
|
||||||
|
if (empty($groupedBy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$metricUpdated = str_replace($options['groupBy'], $groupedBy, $metric);
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = \strtotime($point['time']);
|
||||||
|
$id = \md5($time . '_' . $period['key'] . '_' . $metricUpdated); //Construct unique id for each metric using time, period and metric
|
||||||
|
$value = (!empty($point['value'])) ? $point['value'] : 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'period' => $period['key'],
|
||||||
|
'time' => $time,
|
||||||
|
'metric' => $metricUpdated,
|
||||||
|
'value' => $value,
|
||||||
|
'type' => 0,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $value));
|
||||||
|
}
|
||||||
|
$latestTime[$metric][$period['key']] = $time;
|
||||||
|
} catch (\Exception $e) { // if projects are deleted this might fail
|
||||||
|
Console::warning("Failed to save data for project {$projectId} and metric {$metricUpdated}: {$e->getMessage()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregate MariaDB every 15 minutes
|
||||||
|
* Some of the queries here might contain full-table scans.
|
||||||
|
*/
|
||||||
|
if ($iterations % 30 == 0) { // Every 15 minutes aggregate number of objects in database
|
||||||
|
|
||||||
|
$latestProject = null;
|
||||||
|
|
||||||
|
do { // Loop over all the projects
|
||||||
|
$attempts = 0;
|
||||||
|
$max = 10;
|
||||||
|
$sleep = 1;
|
||||||
|
|
||||||
|
do { // list projects
|
||||||
|
try {
|
||||||
|
$attempts++;
|
||||||
|
$projects = $dbForConsole->find('projects', [], 100, orderAfter:$latestProject);
|
||||||
|
break; // leave the do-while if successful
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
|
||||||
|
if ($attempts >= $max) {
|
||||||
|
throw new \Exception('Failed access console db: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
sleep($sleep);
|
||||||
|
}
|
||||||
|
} while ($attempts < $max);
|
||||||
|
|
||||||
|
if (empty($projects)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestProject = $projects[array_key_last($projects)];
|
||||||
|
|
||||||
|
foreach ($projects as $project) {
|
||||||
|
$projectId = $project->getId();
|
||||||
|
|
||||||
|
// Get total storage
|
||||||
|
$dbForProject->setNamespace('project_' . $projectId . '_internal');
|
||||||
|
$storageTotal = $dbForProject->sum('files', 'sizeOriginal') + $dbForProject->sum('tags', 'size');
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||||
|
$id = \md5($time . '_30m_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'period' => '30m',
|
||||||
|
'time' => $time,
|
||||||
|
'metric' => 'storage.total',
|
||||||
|
'value' => $storageTotal,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $storageTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||||
|
$id = \md5($time . '_1d_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'period' => '1d',
|
||||||
|
'time' => $time,
|
||||||
|
'metric' => 'storage.total',
|
||||||
|
'value' => $storageTotal,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $storageTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
$collections = [
|
||||||
|
'users' => [
|
||||||
|
'namespace' => 'internal',
|
||||||
|
],
|
||||||
|
'collections' => [
|
||||||
|
'metricPrefix' => 'database',
|
||||||
|
'namespace' => 'internal',
|
||||||
|
'subCollections' => [ // Some collections, like collections and later buckets have child collections that need counting
|
||||||
|
'documents' => [
|
||||||
|
'namespace' => 'external',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'files' => [
|
||||||
|
'metricPrefix' => 'storage',
|
||||||
|
'namespace' => 'internal',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($collections as $collection => $options) {
|
||||||
|
try {
|
||||||
|
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}");
|
||||||
|
$count = $dbForProject->count($collection);
|
||||||
|
$dbForProject->setNamespace("project_{$projectId}_internal");
|
||||||
|
$metricPrefix = $options['metricPrefix'] ?? '';
|
||||||
|
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||||
|
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'time' => $time,
|
||||||
|
'period' => '30m',
|
||||||
|
'metric' => $metric,
|
||||||
|
'value' => $count,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $count));
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||||
|
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'time' => $time,
|
||||||
|
'period' => '1d',
|
||||||
|
'metric' => $metric,
|
||||||
|
'value' => $count,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $count));
|
||||||
|
}
|
||||||
|
|
||||||
|
$subCollections = $options['subCollections'] ?? [];
|
||||||
|
|
||||||
|
if (empty($subCollections)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestParent = null;
|
||||||
|
$subCollectionCounts = []; //total project level count of sub collections
|
||||||
|
|
||||||
|
do { // Loop over all the parent collection document for each sub collection
|
||||||
|
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}");
|
||||||
|
$parents = $dbForProject->find($collection, [], 100, orderAfter:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||||
|
|
||||||
|
if (empty($parents)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestParent = $parents[array_key_last($parents)];
|
||||||
|
|
||||||
|
foreach ($parents as $parent) {
|
||||||
|
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
|
||||||
|
$dbForProject->setNamespace("project_{$projectId}_{$subOptions['namespace']}");
|
||||||
|
$count = $dbForProject->count($parent->getId());
|
||||||
|
|
||||||
|
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
|
||||||
|
|
||||||
|
$dbForProject->setNamespace("project_{$projectId}_internal");
|
||||||
|
|
||||||
|
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count";
|
||||||
|
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||||
|
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'time' => $time,
|
||||||
|
'period' => '30m',
|
||||||
|
'metric' => $metric,
|
||||||
|
'value' => $count,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $count));
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||||
|
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'time' => $time,
|
||||||
|
'period' => '1d',
|
||||||
|
'metric' => $metric,
|
||||||
|
'value' => $count,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!empty($parents));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserting project level counts for sub collections like database.documents.count
|
||||||
|
*/
|
||||||
|
foreach ($subCollectionCounts as $subCollection => $count) {
|
||||||
|
$dbForProject->setNamespace("project_{$projectId}_internal");
|
||||||
|
|
||||||
|
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||||
|
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'time' => $time,
|
||||||
|
'period' => '30m',
|
||||||
|
'metric' => $metric,
|
||||||
|
'value' => $count,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $count));
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||||
|
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||||
|
$document = $dbForProject->getDocument('stats', $id);
|
||||||
|
if ($document->isEmpty()) {
|
||||||
|
$dbForProject->createDocument('stats', new Document([
|
||||||
|
'$id' => $id,
|
||||||
|
'time' => $time,
|
||||||
|
'period' => '1d',
|
||||||
|
'metric' => $metric,
|
||||||
|
'value' => $count,
|
||||||
|
'type' => 1,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$dbForProject->updateDocument('stats', $document->getId(),
|
||||||
|
$document->setAttribute('value', $count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception$e) {
|
||||||
|
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!empty($projects));
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterations++;
|
||||||
|
$loopTook = microtime(true) - $loopStart;
|
||||||
|
$now = date('d-m-Y H:i:s', time());
|
||||||
|
|
||||||
|
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||||
|
}, $interval);
|
||||||
|
});
|
|
@ -294,6 +294,26 @@ services:
|
||||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||||
|
|
||||||
|
appwrite-usage:
|
||||||
|
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||||
|
entrypoint: usage
|
||||||
|
container_name: appwrite-usage
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- influxdb
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _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
|
||||||
|
|
||||||
appwrite-schedule:
|
appwrite-schedule:
|
||||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Appwrite\Extend\PDO;
|
|
||||||
use Utopia\App;
|
|
||||||
|
|
||||||
/** @var Utopia\Registry\Registry $register */
|
|
||||||
|
|
||||||
require_once __DIR__.'/init.php';
|
|
||||||
|
|
||||||
$register->set('db', function () {
|
|
||||||
$dbHost = App::getEnv('_APP_DB_HOST', '');
|
|
||||||
$dbUser = App::getEnv('_APP_DB_USER', '');
|
|
||||||
$dbPass = App::getEnv('_APP_DB_PASS', '');
|
|
||||||
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
|
|
||||||
|
|
||||||
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
|
||||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
|
|
||||||
PDO::ATTR_TIMEOUT => 3, // Seconds
|
|
||||||
PDO::ATTR_PERSISTENT => true,
|
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
||||||
));
|
|
||||||
|
|
||||||
return $pdo;
|
|
||||||
});
|
|
||||||
|
|
||||||
$register->set('cache', function () { // Register cache connection
|
|
||||||
$redis = new Redis();
|
|
||||||
$redis->pconnect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', ''));
|
|
||||||
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
|
|
||||||
|
|
||||||
return $redis;
|
|
||||||
});
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use Appwrite\Resque\Worker;
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
|
||||||
require_once __DIR__.'/../workers.php';
|
require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
Console::title('Audits V1 Worker');
|
Console::title('Audits V1 Worker');
|
||||||
Console::success(APP_NAME.' audits worker v1 has started');
|
Console::success(APP_NAME.' audits worker v1 has started');
|
||||||
|
|
|
@ -9,7 +9,7 @@ use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Domains\Domain;
|
use Utopia\Domains\Domain;
|
||||||
|
|
||||||
require_once __DIR__.'/../workers.php';
|
require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
Console::title('Certificates V1 Worker');
|
Console::title('Certificates V1 Worker');
|
||||||
Console::success(APP_NAME.' certificates worker v1 has started');
|
Console::success(APP_NAME.' certificates worker v1 has started');
|
||||||
|
|
|
@ -5,7 +5,7 @@ use Utopia\CLI\Console;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
|
||||||
require_once __DIR__.'/../workers.php';
|
require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
Console::title('Database V1 Worker');
|
Console::title('Database V1 Worker');
|
||||||
Console::success(APP_NAME.' database worker v1 has started'."\n");
|
Console::success(APP_NAME.' database worker v1 has started'."\n");
|
||||||
|
|
|
@ -11,7 +11,7 @@ use Utopia\Abuse\Adapters\TimeLimit;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Audit\Audit;
|
use Utopia\Audit\Audit;
|
||||||
|
|
||||||
require_once __DIR__.'/../workers.php';
|
require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
Console::title('Deletes V1 Worker');
|
Console::title('Deletes V1 Worker');
|
||||||
Console::success(APP_NAME.' deletes worker v1 has started'."\n");
|
Console::success(APP_NAME.' deletes worker v1 has started'."\n");
|
||||||
|
@ -79,6 +79,9 @@ class DeletesV1 extends Worker
|
||||||
$this->deleteCertificates($document);
|
$this->deleteCertificates($document);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DELETE_TYPE_USAGE_STATS:
|
||||||
|
$this->deleteUsageStats($this->args['timestamp1d'], $this->args['timestamp30m']);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Console::error('No delete operation for type: '.$type);
|
Console::error('No delete operation for type: '.$type);
|
||||||
break;
|
break;
|
||||||
|
@ -89,6 +92,29 @@ class DeletesV1 extends Worker
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $timestamp1d
|
||||||
|
* @param int $timestamp30m
|
||||||
|
*/
|
||||||
|
protected function deleteUsageStats(int $timestamp1d, int $timestamp30m) {
|
||||||
|
$this->deleteForProjectIds(function($projectId) use ($timestamp1d, $timestamp30m) {
|
||||||
|
if (!($dbForInternal = $this->getInternalDB($projectId))) {
|
||||||
|
throw new Exception('Failed to get projectDB for project '.$projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Usage stats
|
||||||
|
$this->deleteByGroup('stats', [
|
||||||
|
new Query('time', Query::TYPE_LESSER, [$timestamp1d]),
|
||||||
|
new Query('period', Query::TYPE_EQUAL, ['1d']),
|
||||||
|
], $dbForInternal);
|
||||||
|
|
||||||
|
$this->deleteByGroup('stats', [
|
||||||
|
new Query('time', Query::TYPE_LESSER, [$timestamp30m]),
|
||||||
|
new Query('period', Query::TYPE_EQUAL, ['30m']),
|
||||||
|
], $dbForInternal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Document $document teams document
|
* @param Document $document teams document
|
||||||
* @param string $projectId
|
* @param string $projectId
|
||||||
|
|
|
@ -13,7 +13,7 @@ use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
|
||||||
require_once __DIR__.'/../workers.php';
|
require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
Runtime::enableCoroutine(0);
|
Runtime::enableCoroutine(0);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use Utopia\App;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
|
|
||||||
require_once __DIR__ . '/../workers.php';
|
require_once __DIR__ . '/../init.php';
|
||||||
|
|
||||||
Console::title('Mails V1 Worker');
|
Console::title('Mails V1 Worker');
|
||||||
Console::success(APP_NAME . ' mails worker v1 has started' . "\n");
|
Console::success(APP_NAME . ' mails worker v1 has started' . "\n");
|
||||||
|
|
|
@ -4,7 +4,7 @@ use Appwrite\Resque\Worker;
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
|
||||||
require_once __DIR__.'/../workers.php';
|
require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
Console::title('Webhooks V1 Worker');
|
Console::title('Webhooks V1 Worker');
|
||||||
Console::success(APP_NAME.' webhooks worker v1 has started');
|
Console::success(APP_NAME.' webhooks worker v1 has started');
|
||||||
|
|
3
bin/usage
Executable file
3
bin/usage
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
php /usr/src/code/app/cli.php usage $@
|
|
@ -73,7 +73,6 @@ services:
|
||||||
- mariadb
|
- mariadb
|
||||||
- redis
|
- redis
|
||||||
# - clamav
|
# - clamav
|
||||||
- influxdb
|
|
||||||
entrypoint:
|
entrypoint:
|
||||||
- php
|
- php
|
||||||
- -e
|
- -e
|
||||||
|
@ -112,8 +111,6 @@ services:
|
||||||
- _APP_SMTP_USERNAME
|
- _APP_SMTP_USERNAME
|
||||||
- _APP_SMTP_PASSWORD
|
- _APP_SMTP_PASSWORD
|
||||||
- _APP_USAGE_STATS
|
- _APP_USAGE_STATS
|
||||||
- _APP_INFLUXDB_HOST
|
|
||||||
- _APP_INFLUXDB_PORT
|
|
||||||
- _APP_STORAGE_LIMIT
|
- _APP_STORAGE_LIMIT
|
||||||
- _APP_FUNCTIONS_TIMEOUT
|
- _APP_FUNCTIONS_TIMEOUT
|
||||||
- _APP_FUNCTIONS_CONTAINERS
|
- _APP_FUNCTIONS_CONTAINERS
|
||||||
|
@ -349,6 +346,37 @@ services:
|
||||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||||
|
|
||||||
|
appwrite-usage:
|
||||||
|
entrypoint:
|
||||||
|
- php
|
||||||
|
- -e
|
||||||
|
- /usr/src/code/app/cli.php
|
||||||
|
- usage
|
||||||
|
container_name: appwrite-usage
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
args:
|
||||||
|
- DEBUG=false
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
volumes:
|
||||||
|
- ./app:/usr/src/code/app
|
||||||
|
- ./src:/usr/src/code/src
|
||||||
|
- ./dev:/usr/local/dev
|
||||||
|
depends_on:
|
||||||
|
- influxdb
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_INFLUXDB_HOST
|
||||||
|
- _APP_INFLUXDB_PORT
|
||||||
|
- _APP_USAGE_SYNC_INTERVAL
|
||||||
|
|
||||||
appwrite-schedule:
|
appwrite-schedule:
|
||||||
entrypoint: schedule
|
entrypoint: schedule
|
||||||
container_name: appwrite-schedule
|
container_name: appwrite-schedule
|
||||||
|
|
|
@ -21,6 +21,28 @@ class OpenAPI3 extends Format
|
||||||
return 'Open API 3';
|
return 'Open API 3';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Used Models
|
||||||
|
*
|
||||||
|
* Recursively get all used models
|
||||||
|
*
|
||||||
|
* @param object $model
|
||||||
|
* @param array $models
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function getUsedModels($model, array &$usedModels)
|
||||||
|
{
|
||||||
|
if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||||
|
$usedModels[] = $model;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!is_object($model)) return;
|
||||||
|
foreach ($model->getRules() as $rule) {
|
||||||
|
$this->getUsedModels($rule['type'], $usedModels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse
|
* Parse
|
||||||
*
|
*
|
||||||
|
@ -352,11 +374,7 @@ class OpenAPI3 extends Format
|
||||||
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
||||||
}
|
}
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
foreach ($model->getRules() as $rule) {
|
$this->getUsedModels($model, $usedModels);
|
||||||
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
|
|
||||||
$usedModels[] = $rule['type'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
if (!in_array($model->getType(), $usedModels) && $model->getType() !== 'error') {
|
if (!in_array($model->getType(), $usedModels) && $model->getType() !== 'error') {
|
||||||
|
|
|
@ -21,6 +21,28 @@ class Swagger2 extends Format
|
||||||
return 'Swagger 2';
|
return 'Swagger 2';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Used Models
|
||||||
|
*
|
||||||
|
* Recursively get all used models
|
||||||
|
*
|
||||||
|
* @param object $model
|
||||||
|
* @param array $models
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function getUsedModels($model, array &$usedModels)
|
||||||
|
{
|
||||||
|
if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||||
|
$usedModels[] = $model;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!is_object($model)) return;
|
||||||
|
foreach ($model->getRules() as $rule) {
|
||||||
|
$this->getUsedModels($rule['type'], $usedModels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse
|
* Parse
|
||||||
*
|
*
|
||||||
|
@ -354,15 +376,9 @@ class Swagger2 extends Format
|
||||||
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
||||||
}
|
}
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
foreach ($model->getRules() as $rule) {
|
$this->getUsedModels($model, $usedModels);
|
||||||
if (
|
|
||||||
in_array($model->getType(), $usedModels)
|
|
||||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
|
||||||
) {
|
|
||||||
$usedModels[] = $rule['type'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->models as $model) {
|
foreach ($this->models as $model) {
|
||||||
if (!in_array($model->getType(), $usedModels)) {
|
if (!in_array($model->getType(), $usedModels)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -94,7 +94,7 @@ class Stats
|
||||||
$functionExecutionTime = $this->params['functionExecutionTime'] ?? 0;
|
$functionExecutionTime = $this->params['functionExecutionTime'] ?? 0;
|
||||||
$functionStatus = $this->params['functionStatus'] ?? '';
|
$functionStatus = $this->params['functionStatus'] ?? '';
|
||||||
|
|
||||||
$tags = ",project={$projectId},version=" . App::getEnv('_APP_VERSION', 'UNKNOWN');
|
$tags = ",projectId={$projectId},version=" . App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||||
|
|
||||||
// the global namespace is prepended to every key (optional)
|
// the global namespace is prepended to every key (optional)
|
||||||
$this->statsd->setNamespace($this->namespace);
|
$this->statsd->setNamespace($this->namespace);
|
||||||
|
@ -112,7 +112,70 @@ class Stats
|
||||||
$this->statsd->count('network.outbound' . $tags, $networkResponseSize);
|
$this->statsd->count('network.outbound' . $tags, $networkResponseSize);
|
||||||
$this->statsd->count('network.all' . $tags, $networkRequestSize + $networkResponseSize);
|
$this->statsd->count('network.all' . $tags, $networkRequestSize + $networkResponseSize);
|
||||||
|
|
||||||
|
$dbMetrics = [
|
||||||
|
'database.collections.create',
|
||||||
|
'database.collections.read',
|
||||||
|
'database.collections.update',
|
||||||
|
'database.collections.delete',
|
||||||
|
'database.documents.create',
|
||||||
|
'database.documents.read',
|
||||||
|
'database.documents.update',
|
||||||
|
'database.documents.delete',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($dbMetrics as $metric) {
|
||||||
|
$value = $this->params[$metric] ?? 0;
|
||||||
|
if ($value >= 1) {
|
||||||
|
$tags = ",projectId={$projectId},collectionId=" . ($this->params['collectionId'] ?? '');
|
||||||
|
$this->statsd->increment($metric . $tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$storageMertics = [
|
||||||
|
'storage.files.create',
|
||||||
|
'storage.files.read',
|
||||||
|
'storage.files.update',
|
||||||
|
'storage.files.delete',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($storageMertics as $metric) {
|
||||||
|
$value = $this->params[$metric] ?? 0;
|
||||||
|
if ($value >= 1) {
|
||||||
|
$tags = ",projectId={$projectId},bucketId=" . ($this->params['bucketId'] ?? '');
|
||||||
|
$this->statsd->increment($metric . $tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$usersMetrics = [
|
||||||
|
'users.create',
|
||||||
|
'users.read',
|
||||||
|
'users.update',
|
||||||
|
'users.delete',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($usersMetrics as $metric) {
|
||||||
|
$value = $this->params[$metric] ?? 0;
|
||||||
|
if ($value >= 1) {
|
||||||
|
$tags = ",projectId={$projectId}";
|
||||||
|
$this->statsd->increment($metric . $tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionsMetrics = [
|
||||||
|
'users.sessions.create',
|
||||||
|
'users.sessions.delete',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($sessionsMetrics as $metric) {
|
||||||
|
$value = $this->params[$metric] ?? 0;
|
||||||
|
if ($value >= 1) {
|
||||||
|
$tags = ",projectId={$projectId},provider=". ($this->params['provider'] ?? '');
|
||||||
|
$this->statsd->count($metric . $tags, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($storage >= 1) {
|
if ($storage >= 1) {
|
||||||
|
$tags = ",projectId={$projectId},bucketId=" . ($this->params['bucketId'] ?? '');
|
||||||
$this->statsd->count('storage.all' . $tags, $storage);
|
$this->statsd->count('storage.all' . $tags, $storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ use Appwrite\Utopia\Response\Model\Team;
|
||||||
use Appwrite\Utopia\Response\Model\Locale;
|
use Appwrite\Utopia\Response\Model\Locale;
|
||||||
use Appwrite\Utopia\Response\Model\Log;
|
use Appwrite\Utopia\Response\Model\Log;
|
||||||
use Appwrite\Utopia\Response\Model\Membership;
|
use Appwrite\Utopia\Response\Model\Membership;
|
||||||
|
use Appwrite\Utopia\Response\Model\Metric;
|
||||||
use Appwrite\Utopia\Response\Model\Permissions;
|
use Appwrite\Utopia\Response\Model\Permissions;
|
||||||
use Appwrite\Utopia\Response\Model\Phone;
|
use Appwrite\Utopia\Response\Model\Phone;
|
||||||
use Appwrite\Utopia\Response\Model\Platform;
|
use Appwrite\Utopia\Response\Model\Platform;
|
||||||
|
@ -43,6 +44,13 @@ use Appwrite\Utopia\Response\Model\Token;
|
||||||
use Appwrite\Utopia\Response\Model\Webhook;
|
use Appwrite\Utopia\Response\Model\Webhook;
|
||||||
use Appwrite\Utopia\Response\Model\Preferences;
|
use Appwrite\Utopia\Response\Model\Preferences;
|
||||||
use Appwrite\Utopia\Response\Model\Mock; // Keep last
|
use Appwrite\Utopia\Response\Model\Mock; // Keep last
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageDatabase;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageFunctions;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageProject;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageStorage;
|
||||||
|
use Appwrite\Utopia\Response\Model\UsageUsers;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,8 +64,17 @@ class Response extends SwooleResponse
|
||||||
const MODEL_LOG = 'log';
|
const MODEL_LOG = 'log';
|
||||||
const MODEL_LOG_LIST = 'logList';
|
const MODEL_LOG_LIST = 'logList';
|
||||||
const MODEL_ERROR = 'error';
|
const MODEL_ERROR = 'error';
|
||||||
|
const MODEL_METRIC = 'metric';
|
||||||
|
const MODEL_METRIC_LIST = 'metricList';
|
||||||
const MODEL_ERROR_DEV = 'errorDev';
|
const MODEL_ERROR_DEV = 'errorDev';
|
||||||
const MODEL_BASE_LIST = 'baseList';
|
const MODEL_BASE_LIST = 'baseList';
|
||||||
|
const MODEL_USAGE_DATABASE = 'usageDatabase';
|
||||||
|
const MODEL_USAGE_COLLECTION = 'usageCollection';
|
||||||
|
const MODEL_USAGE_USERS = 'usageUsers';
|
||||||
|
const MODEL_USAGE_BUCKETS = 'usageBuckets';
|
||||||
|
const MODEL_USAGE_STORAGE = 'usageStorage';
|
||||||
|
const MODEL_USAGE_FUNCTIONS = 'usageFunctions';
|
||||||
|
const MODEL_USAGE_PROJECT = 'usageProject';
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
const MODEL_COLLECTION = 'collection';
|
const MODEL_COLLECTION = 'collection';
|
||||||
|
@ -125,6 +142,7 @@ class Response extends SwooleResponse
|
||||||
// Deprecated
|
// Deprecated
|
||||||
const MODEL_PERMISSIONS = 'permissions';
|
const MODEL_PERMISSIONS = 'permissions';
|
||||||
const MODEL_RULE = 'rule';
|
const MODEL_RULE = 'rule';
|
||||||
|
const MODEL_TASK = 'task';
|
||||||
|
|
||||||
// Tests (keep last)
|
// Tests (keep last)
|
||||||
const MODEL_MOCK = 'mock';
|
const MODEL_MOCK = 'mock';
|
||||||
|
@ -176,6 +194,7 @@ class Response extends SwooleResponse
|
||||||
->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE))
|
->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE))
|
||||||
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
|
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
|
||||||
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
|
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
|
||||||
|
->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false))
|
||||||
// Entities
|
// Entities
|
||||||
->setModel(new Collection())
|
->setModel(new Collection())
|
||||||
->setModel(new Attribute())
|
->setModel(new Attribute())
|
||||||
|
@ -204,6 +223,14 @@ class Response extends SwooleResponse
|
||||||
->setModel(new Language())
|
->setModel(new Language())
|
||||||
->setModel(new Currency())
|
->setModel(new Currency())
|
||||||
->setModel(new Phone())
|
->setModel(new Phone())
|
||||||
|
->setModel(new Metric())
|
||||||
|
->setModel(new UsageDatabase())
|
||||||
|
->setModel(new UsageCollection())
|
||||||
|
->setModel(new UsageUsers())
|
||||||
|
->setModel(new UsageStorage())
|
||||||
|
->setModel(new UsageBuckets())
|
||||||
|
->setModel(new UsageFunctions())
|
||||||
|
->setModel(new UsageProject())
|
||||||
// Verification
|
// Verification
|
||||||
// Recovery
|
// Recovery
|
||||||
// Tests (keep last)
|
// Tests (keep last)
|
||||||
|
|
47
src/Appwrite/Utopia/Response/Model/Metric.php
Normal file
47
src/Appwrite/Utopia/Response/Model/Metric.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
class Metric extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('value', [
|
||||||
|
'type' => self::TYPE_INTEGER,
|
||||||
|
'description' => 'The value of this metric at the timestamp.',
|
||||||
|
'default' => -1,
|
||||||
|
'example' => 1,
|
||||||
|
])
|
||||||
|
->addRule('timestamp', [
|
||||||
|
'type' => self::TYPE_INTEGER,
|
||||||
|
'description' => 'The UNIX timestamp at which this metric was aggregated.',
|
||||||
|
'default' => 0,
|
||||||
|
'example' => 1592981250
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'Metric';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Collection
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_METRIC;
|
||||||
|
}
|
||||||
|
}
|
77
src/Appwrite/Utopia/Response/Model/UsageBuckets.php
Normal file
77
src/Appwrite/Utopia/Response/Model/UsageBuckets.php
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageBuckets extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('files.count', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for total number of files in this bucket.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('files.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for files created.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('files.read', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for files read.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('files.update', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for files updated.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('files.delete', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for files deleted.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'UsageBuckets';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_BUCKETS;
|
||||||
|
}
|
||||||
|
}
|
77
src/Appwrite/Utopia/Response/Model/UsageCollection.php
Normal file
77
src/Appwrite/Utopia/Response/Model/UsageCollection.php
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageCollection extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('documents.count', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for total number of documents.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents created.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.read', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents read.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.update', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents updated.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.delete', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents deleted.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'UsageCollection';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_COLLECTION;
|
||||||
|
}
|
||||||
|
}
|
112
src/Appwrite/Utopia/Response/Model/UsageDatabase.php
Normal file
112
src/Appwrite/Utopia/Response/Model/UsageDatabase.php
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageDatabase extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('documents.count', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for total number of documents.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('collections.count', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for total number of collections.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents created.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.read', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents read.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.update', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents updated.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents.delete', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for documents deleted.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('collections.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for collections created.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('collections.read', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for collections read.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('collections.update', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for collections updated.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('collections.delete', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for collections delete.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'UsageDatabase';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_DATABASE;
|
||||||
|
}
|
||||||
|
}
|
63
src/Appwrite/Utopia/Response/Model/UsageFunctions.php
Normal file
63
src/Appwrite/Utopia/Response/Model/UsageFunctions.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageFunctions extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('functions.executions', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for function executions.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('functions.failures', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for function execution failures.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('functions.compute', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for function execution duration.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'UsageFunctions';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_FUNCTIONS;
|
||||||
|
}
|
||||||
|
}
|
91
src/Appwrite/Utopia/Response/Model/UsageProject.php
Normal file
91
src/Appwrite/Utopia/Response/Model/UsageProject.php
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageProject extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('requests', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for number of requests.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('network', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for consumed bandwidth.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('functions', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for function executions.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('documents', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for number of documents.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('collections', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for number of collections.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('users', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for number of users.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('storage', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'UsageProject';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_PROJECT;
|
||||||
|
}
|
||||||
|
}
|
56
src/Appwrite/Utopia/Response/Model/UsageStorage.php
Normal file
56
src/Appwrite/Utopia/Response/Model/UsageStorage.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageStorage extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('storage', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('files', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for total number of files.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'StorageUsage';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_STORAGE;
|
||||||
|
}
|
||||||
|
}
|
98
src/Appwrite/Utopia/Response/Model/UsageUsers.php
Normal file
98
src/Appwrite/Utopia/Response/Model/UsageUsers.php
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class UsageUsers extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('range', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The time range of the usage stats.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '30d',
|
||||||
|
])
|
||||||
|
->addRule('users.count', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for total number of users.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('users.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for users created.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('users.read', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for users read.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('users.update', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for users updated.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('users.delete', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for users deleted.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('sessions.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for sessions created.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('sessions.provider.create', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
->addRule('sessions.delete', [
|
||||||
|
'type' => Response::MODEL_METRIC_LIST,
|
||||||
|
'description' => 'Aggregated stats for sessions deleted.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => new stdClass,
|
||||||
|
'array' => true
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName():string
|
||||||
|
{
|
||||||
|
return 'UsageUsers';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType():string
|
||||||
|
{
|
||||||
|
return Response::MODEL_USAGE_USERS;
|
||||||
|
}
|
||||||
|
}
|
23
tests/e2e/Scopes/SideConsole.php
Normal file
23
tests/e2e/Scopes/SideConsole.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Scopes;
|
||||||
|
|
||||||
|
trait SideConsole
|
||||||
|
{
|
||||||
|
public function getHeaders():array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'cookie' => 'a_session_console='. $this->getRoot()['session'],
|
||||||
|
'x-appwrite-mode' => 'admin'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSide()
|
||||||
|
{
|
||||||
|
return 'console';
|
||||||
|
}
|
||||||
|
}
|
125
tests/e2e/Services/Database/DatabaseConsoleClientTest.php
Normal file
125
tests/e2e/Services/Database/DatabaseConsoleClientTest.php
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Services\Database;
|
||||||
|
|
||||||
|
use Tests\E2E\Scopes\Scope;
|
||||||
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
use Tests\E2E\Client;
|
||||||
|
use Tests\E2E\Scopes\SideConsole;
|
||||||
|
|
||||||
|
class DatabaseConsoleClientTest extends Scope
|
||||||
|
{
|
||||||
|
use ProjectCustom;
|
||||||
|
use SideConsole;
|
||||||
|
|
||||||
|
public function testCreateCollection():array
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'collectionId' => 'unique()',
|
||||||
|
'name' => 'Movies',
|
||||||
|
'read' => ['role:all'],
|
||||||
|
'write' => ['role:all'],
|
||||||
|
'permission' => 'document',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($movies['headers']['status-code'], 201);
|
||||||
|
$this->assertEquals($movies['body']['name'], 'Movies');
|
||||||
|
|
||||||
|
return ['moviesId' => $movies['body']['$id']];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDatabaseUsage()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/database/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '32h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/database/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']), 11);
|
||||||
|
$this->assertEquals($response['body']['range'], '24h');
|
||||||
|
$this->assertIsArray($response['body']['documents.count']);
|
||||||
|
$this->assertIsArray($response['body']['collections.count']);
|
||||||
|
$this->assertIsArray($response['body']['documents.create']);
|
||||||
|
$this->assertIsArray($response['body']['documents.read']);
|
||||||
|
$this->assertIsArray($response['body']['documents.update']);
|
||||||
|
$this->assertIsArray($response['body']['documents.delete']);
|
||||||
|
$this->assertIsArray($response['body']['collections.create']);
|
||||||
|
$this->assertIsArray($response['body']['collections.read']);
|
||||||
|
$this->assertIsArray($response['body']['collections.update']);
|
||||||
|
$this->assertIsArray($response['body']['collections.delete']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateCollection
|
||||||
|
*/
|
||||||
|
public function testGetCollectionUsage(array $data)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '32h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/database/randomCollectionId/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '24h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 404);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/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']), 6);
|
||||||
|
$this->assertEquals($response['body']['range'], '24h');
|
||||||
|
$this->assertIsArray($response['body']['documents.count']);
|
||||||
|
$this->assertIsArray($response['body']['documents.create']);
|
||||||
|
$this->assertIsArray($response['body']['documents.read']);
|
||||||
|
$this->assertIsArray($response['body']['documents.update']);
|
||||||
|
$this->assertIsArray($response['body']['documents.delete']);
|
||||||
|
}
|
||||||
|
}
|
91
tests/e2e/Services/Functions/FunctionsConsoleClientTest.php
Normal file
91
tests/e2e/Services/Functions/FunctionsConsoleClientTest.php
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Services\Functions;
|
||||||
|
|
||||||
|
use Tests\E2E\Scopes\Scope;
|
||||||
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
use Tests\E2E\Client;
|
||||||
|
use Tests\E2E\Scopes\SideConsole;
|
||||||
|
|
||||||
|
class FunctionsConsoleClientTest extends Scope
|
||||||
|
{
|
||||||
|
use ProjectCustom;
|
||||||
|
use SideConsole;
|
||||||
|
|
||||||
|
public function testCreateFunction():array
|
||||||
|
{
|
||||||
|
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'functionId' => 'unique()',
|
||||||
|
'name' => 'Test',
|
||||||
|
'execute' => ['user:'.$this->getUser()['$id']],
|
||||||
|
'runtime' => 'php-8.0',
|
||||||
|
'vars' => [
|
||||||
|
'funcKey1' => 'funcValue1',
|
||||||
|
'funcKey2' => 'funcValue2',
|
||||||
|
'funcKey3' => 'funcValue3',
|
||||||
|
],
|
||||||
|
'events' => [
|
||||||
|
'account.create',
|
||||||
|
'account.delete',
|
||||||
|
],
|
||||||
|
'schedule' => '0 0 1 1 *',
|
||||||
|
'timeout' => 10,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $function['headers']['status-code']);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'functionId' => $function['body']['$id']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateFunction
|
||||||
|
*/
|
||||||
|
public function testGetCollectionUsage(array $data)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '232h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(400, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/functions/randomFunctionId/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '24h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(404, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/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']), 4);
|
||||||
|
$this->assertEquals($response['body']['range'], '24h');
|
||||||
|
$this->assertIsArray($response['body']['functions.executions']);
|
||||||
|
$this->assertIsArray($response['body']['functions.failures']);
|
||||||
|
$this->assertIsArray($response['body']['functions.compute']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -215,24 +215,16 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
], $this->getHeaders()));
|
], $this->getHeaders()));
|
||||||
|
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
$this->assertEquals(count($response['body']), 8);
|
||||||
$this->assertNotEmpty($response['body']);
|
$this->assertNotEmpty($response['body']);
|
||||||
$this->assertArrayHasKey('collections', $response['body']);
|
$this->assertEquals('30d', $response['body']['range']);
|
||||||
$this->assertArrayHasKey('documents', $response['body']);
|
$this->assertIsArray($response['body']['requests']);
|
||||||
$this->assertArrayHasKey('network', $response['body']);
|
$this->assertIsArray($response['body']['network']);
|
||||||
$this->assertArrayHasKey('requests', $response['body']);
|
$this->assertIsArray($response['body']['functions']);
|
||||||
$this->assertArrayHasKey('storage', $response['body']);
|
$this->assertIsArray($response['body']['documents']);
|
||||||
$this->assertArrayHasKey('users', $response['body']);
|
$this->assertIsArray($response['body']['collections']);
|
||||||
$this->assertIsArray($response['body']['collections']['data']);
|
$this->assertIsArray($response['body']['users']);
|
||||||
$this->assertIsInt($response['body']['collections']['total']);
|
$this->assertIsArray($response['body']['storage']);
|
||||||
$this->assertIsArray($response['body']['documents']['data']);
|
|
||||||
$this->assertIsInt($response['body']['documents']['total']);
|
|
||||||
$this->assertIsArray($response['body']['network']['data']);
|
|
||||||
$this->assertIsInt($response['body']['network']['total']);
|
|
||||||
$this->assertIsArray($response['body']['requests']['data']);
|
|
||||||
$this->assertIsInt($response['body']['requests']['total']);
|
|
||||||
$this->assertIsInt($response['body']['storage']['total']);
|
|
||||||
$this->assertIsArray($response['body']['users']['data']);
|
|
||||||
$this->assertIsInt($response['body']['users']['total']);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for FAILURE
|
* Test for FAILURE
|
||||||
|
|
|
@ -2,13 +2,91 @@
|
||||||
|
|
||||||
namespace Tests\E2E\Services\Storage;
|
namespace Tests\E2E\Services\Storage;
|
||||||
|
|
||||||
|
use Tests\E2E\Client;
|
||||||
use Tests\E2E\Scopes\Scope;
|
use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\ProjectConsole;
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
use Tests\E2E\Scopes\SideClient;
|
use Tests\E2E\Scopes\SideConsole;
|
||||||
|
|
||||||
class StorageConsoleClientTest extends Scope
|
class StorageConsoleClientTest extends Scope
|
||||||
{
|
{
|
||||||
|
use SideConsole;
|
||||||
use StorageBase;
|
use StorageBase;
|
||||||
use ProjectConsole;
|
use ProjectCustom;
|
||||||
use SideClient;
|
|
||||||
|
public function testGetStorageUsage()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/storage/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '32h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/storage/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']), 3);
|
||||||
|
$this->assertEquals($response['body']['range'], '24h');
|
||||||
|
$this->assertIsArray($response['body']['storage']);
|
||||||
|
$this->assertIsArray($response['body']['files']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStorageBucketUsage()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/storage/default/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'range' => '32h'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
// TODO: Uncomment once we implement check for missing bucketId in the usage endpoint.
|
||||||
|
|
||||||
|
// $response = $this->client->call(Client::METHOD_GET, '/storage/randomBucketId/usage', array_merge([
|
||||||
|
// 'content-type' => 'application/json',
|
||||||
|
// 'x-appwrite-project' => $this->getProject()['$id']
|
||||||
|
// ], $this->getHeaders()), [
|
||||||
|
// 'range' => '24h'
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// $this->assertEquals($response['headers']['status-code'], 404);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/storage/default/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']), 6);
|
||||||
|
$this->assertEquals($response['body']['range'], '24h');
|
||||||
|
$this->assertIsArray($response['body']['files.count']);
|
||||||
|
$this->assertIsArray($response['body']['files.create']);
|
||||||
|
$this->assertIsArray($response['body']['files.read']);
|
||||||
|
$this->assertIsArray($response['body']['files.update']);
|
||||||
|
$this->assertIsArray($response['body']['files.delete']);
|
||||||
|
}
|
||||||
}
|
}
|
83
tests/e2e/Services/Users/UsersConsoleClientTest.php
Normal file
83
tests/e2e/Services/Users/UsersConsoleClientTest.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Services\Users;
|
||||||
|
|
||||||
|
use Tests\E2E\Scopes\Scope;
|
||||||
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
use Tests\E2E\Client;
|
||||||
|
use Tests\E2E\Scopes\SideConsole;
|
||||||
|
|
||||||
|
class UsersConsoleClientTest extends Scope
|
||||||
|
{
|
||||||
|
use ProjectCustom;
|
||||||
|
use SideConsole;
|
||||||
|
|
||||||
|
public function testGetUsersUsage()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$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' => 'email'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 200);
|
||||||
|
$this->assertEquals(count($response['body']), 9);
|
||||||
|
$this->assertEquals($response['body']['range'], '24h');
|
||||||
|
$this->assertIsArray($response['body']['users.count']);
|
||||||
|
$this->assertIsArray($response['body']['users.create']);
|
||||||
|
$this->assertIsArray($response['body']['users.read']);
|
||||||
|
$this->assertIsArray($response['body']['users.update']);
|
||||||
|
$this->assertIsArray($response['body']['users.delete']);
|
||||||
|
$this->assertIsArray($response['body']['sessions.create']);
|
||||||
|
$this->assertIsArray($response['body']['sessions.provider.create']);
|
||||||
|
$this->assertIsArray($response['body']['sessions.delete']);
|
||||||
|
|
||||||
|
$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']['users.count']);
|
||||||
|
$this->assertIsArray($response['body']['users.create']);
|
||||||
|
$this->assertIsArray($response['body']['users.read']);
|
||||||
|
$this->assertIsArray($response['body']['users.update']);
|
||||||
|
$this->assertIsArray($response['body']['users.delete']);
|
||||||
|
$this->assertIsArray($response['body']['sessions.create']);
|
||||||
|
$this->assertIsArray($response['body']['sessions.provider.create']);
|
||||||
|
$this->assertIsArray($response['body']['sessions.delete']);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue