mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 08:58:35 +00:00
Merge pull request #1654 from appwrite/feat-db-refactor-ui-fixes
feat(refactor-db): prep the API for UI work
This commit is contained in:
commit
28e345064c
61 changed files with 5754 additions and 727 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -218,6 +218,8 @@ App::post('/v1/account/sessions')
|
|||
->setParam('userId', $profile->getId())
|
||||
->setParam('event', 'account.sessions.create')
|
||||
->setParam('resource', 'user/' . $profile->getId())
|
||||
->setParam('userEmail', $profile->getAttribute('email', ''))
|
||||
->setParam('userName', $profile->getAttribute('name', ''))
|
||||
;
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
|
|
@ -1228,17 +1230,19 @@ App::get('/v1/account/logs')
|
|||
|
||||
$detector = new Detector($log['userAgent']);
|
||||
|
||||
$output[$i] = new Document(array_merge([
|
||||
'event' => $log['event'],
|
||||
'ip' => $log['ip'],
|
||||
'time' => $log['time'],
|
||||
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
|
||||
$output[$i] = new Document(array_merge(
|
||||
$log->getArrayCopy(),
|
||||
$log['data'],
|
||||
$detector->getOS(),
|
||||
$detector->getClient(),
|
||||
$detector->getDevice()
|
||||
));
|
||||
|
||||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
|
|||
'array' => $array,
|
||||
'format' => $format,
|
||||
'formatOptions' => $formatOptions,
|
||||
'filters' => $filters,
|
||||
]);
|
||||
|
||||
$dbForInternal->checkAttribute($collection, $attribute);
|
||||
|
|
@ -289,7 +290,7 @@ App::get('/v1/database/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -323,13 +324,16 @@ App::get('/v1/database/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
|
@ -337,22 +341,38 @@ App::get('/v1/database/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
// TODO@kodumbeats explore performance if query is ordered by time ASC
|
||||
$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"],
|
||||
'documentsCount' => $stats["database.documents.count"],
|
||||
'collectionsCount' => $stats["database.collections.count"],
|
||||
'documentsCreate' => $stats["database.documents.create"],
|
||||
'documentsRead' => $stats["database.documents.read"],
|
||||
'documentsUpdate' => $stats["database.documents.update"],
|
||||
'documentsDelete' => $stats["database.documents.delete"],
|
||||
'collectionsCreate' => $stats["database.collections.create"],
|
||||
'collectionsRead' => $stats["database.collections.read"],
|
||||
'collectionsUpdate' => $stats["database.collections.update"],
|
||||
'collectionsDelete' => $stats["database.collections.delete"],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -388,7 +408,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
|
||||
$usage = [];
|
||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -417,13 +437,16 @@ App::get('/v1/database/:collectionId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
|
@ -431,17 +454,32 @@ App::get('/v1/database/:collectionId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$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"]
|
||||
'documentsCount' => $stats["database.collections.$collectionId.documents.count"],
|
||||
'documentsCreate' => $stats["database.collections.$collectionId.documents.create"],
|
||||
'documentsRead' => $stats["database.collections.$collectionId.documents.read"],
|
||||
'documentsUpdate' => $stats["database.collections.$collectionId.documents.update"],
|
||||
'documentsDelete' => $stats["database.collections.$collectionId.documents.delete"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -650,6 +688,8 @@ App::delete('/v1/database/collections/:collectionId')
|
|||
throw new Exception('Failed to remove collection from DB', 500);
|
||||
}
|
||||
|
||||
$dbForExternal->deleteCachedCollection($collection->getId());
|
||||
|
||||
$deletes
|
||||
->setParam('type', DELETE_TYPE_DOCUMENT)
|
||||
->setParam('document', $collection)
|
||||
|
|
@ -1013,7 +1053,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
|||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
// Ensure attribute default is within range
|
||||
$min = (is_null($min)) ? PHP_FLOAT_MIN : \floatval($min);
|
||||
$min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min);
|
||||
$max = (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max);
|
||||
|
||||
if ($min > $max) {
|
||||
|
|
@ -1771,6 +1811,113 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$response->dynamic($document, Response::MODEL_DOCUMENT);
|
||||
});
|
||||
|
||||
App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
||||
->desc('List Document Logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.method', 'listDocumentLogs')
|
||||
->label('sdk.description', '/docs/references/database/get-document-logs.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('collectionId', '', new UID(), 'Collection unique ID.')
|
||||
->param('documentId', null, new UID(), 'Document unique ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($collectionId, $documentId, $limit, $offset, $response, $dbForInternal, $dbForExternal, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
/** @var Utopia\Database\Database $dbForExternal */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
|
||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForInternal);
|
||||
$resource = 'document/'.$document->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
foreach ($logs as $i => &$log) {
|
||||
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
|
||||
|
||||
$dd = new DeviceDetector($log['userAgent']);
|
||||
|
||||
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$dd->parse();
|
||||
|
||||
$os = $dd->getOs();
|
||||
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
|
||||
$osName = (isset($os['name'])) ? $os['name'] : '';
|
||||
$osVersion = (isset($os['version'])) ? $os['version'] : '';
|
||||
|
||||
$client = $dd->getClient();
|
||||
$clientType = (isset($client['type'])) ? $client['type'] : '';
|
||||
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
|
||||
$clientName = (isset($client['name'])) ? $client['name'] : '';
|
||||
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
|
||||
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
|
||||
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
|
||||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
'userId' => $log['userId'],
|
||||
'userEmail' => $log['data']['userEmail'] ?? null,
|
||||
'userName' => $log['data']['userName'] ?? null,
|
||||
'mode' => $log['data']['mode'] ?? null,
|
||||
'ip' => $log['ip'],
|
||||
'time' => $log['time'],
|
||||
|
||||
'osCode' => $osCode,
|
||||
'osName' => $osName,
|
||||
'osVersion' => $osVersion,
|
||||
'clientType' => $clientType,
|
||||
'clientCode' => $clientCode,
|
||||
'clientName' => $clientName,
|
||||
'clientVersion' => $clientVersion,
|
||||
'clientEngine' => $clientEngine,
|
||||
'clientEngineVersion' => $clientEngineVersion,
|
||||
'deviceName' => $dd->getDeviceName(),
|
||||
'deviceBrand' => $dd->getBrandName(),
|
||||
'deviceModel' => $dd->getModel(),
|
||||
]);
|
||||
|
||||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
}
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'sum' => $audit->countLogsByResource($resource),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||
->desc('Update Document')
|
||||
->groups(['api', 'database'])
|
||||
|
|
@ -1925,6 +2072,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
}
|
||||
|
||||
$dbForExternal->deleteDocument($collectionId, $documentId);
|
||||
$dbForExternal->deleteCachedDocument($collectionId, $documentId);
|
||||
|
||||
$usage
|
||||
->setParam('database.documents.delete', 1)
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ App::get('/v1/functions/:functionId/usage')
|
|||
|
||||
$usage = [];
|
||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -203,12 +203,15 @@ App::get('/v1/functions/:functionId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
|
|
@ -217,15 +220,30 @@ App::get('/v1/functions/:functionId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'functions.executions' => $stats["functions.$functionId.executions"],
|
||||
'functions.failures' => $stats["functions.$functionId.failures"],
|
||||
'functions.compute' => $stats["functions.$functionId.compute"]
|
||||
'functionsExecutions' => $stats["functions.$functionId.executions"],
|
||||
'functionsFailures' => $stats["functions.$functionId.failures"],
|
||||
'functionsCompute' => $stats["functions.$functionId.compute"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -288,13 +288,16 @@ App::get('/v1/projects/:projectId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
|
@ -302,8 +305,23 @@ App::get('/v1/projects/:projectId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
|
|
|
|||
|
|
@ -670,7 +670,7 @@ App::get('/v1/storage/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -690,19 +690,22 @@ App::get('/v1/storage/usage')
|
|||
];
|
||||
|
||||
$metrics = [
|
||||
"storage.total",
|
||||
"storage.files.count"
|
||||
'storage.total',
|
||||
'storage.files.count'
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
|
@ -710,8 +713,23 @@ App::get('/v1/storage/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
|
|
@ -746,7 +764,7 @@ App::get('/v1/storage/:bucketId/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -775,13 +793,16 @@ App::get('/v1/storage/:bucketId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
|
@ -789,17 +810,32 @@ App::get('/v1/storage/:bucketId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$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"]
|
||||
'filesCount' => $stats["storage.buckets.$bucketId.files.count"],
|
||||
'filesCreate' => $stats["storage.buckets.$bucketId.files.create"],
|
||||
'filesRead' => $stats["storage.buckets.$bucketId.files.read"],
|
||||
'filesUpdate' => $stats["storage.buckets.$bucketId.files.update"],
|
||||
'filesDelete' => $stats["storage.buckets.$bucketId.files.delete"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -787,7 +787,7 @@ App::get('/v1/users/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
|
@ -819,12 +819,15 @@ App::get('/v1/users/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
|
|
@ -833,20 +836,36 @@ App::get('/v1/users/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$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"]
|
||||
'usersCount' => $stats["users.count"],
|
||||
'usersCreate' => $stats["users.create"],
|
||||
'usersRead' => $stats["users.read"],
|
||||
'usersUpdate' => $stats["users.update"],
|
||||
'usersDelete' => $stats["users.delete"],
|
||||
'sessionsCreate' => $stats["users.sessions.create"],
|
||||
'sessionsProviderCreate' => $stats["users.sessions.$provider.create"],
|
||||
'sessionsDelete' => $stats["users.sessions.delete"]
|
||||
]);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,6 +218,10 @@ App::get('/console/database/collection')
|
|||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'database.listCollectionLogs')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.id}}',
|
||||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
|
||||
|
|
@ -245,14 +249,45 @@ App::get('/console/database/document')
|
|||
->action(function ($collection, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'database.listDocumentLogs')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'document-id' => '{{router.params.id}}',
|
||||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
$searchFiles = new View(__DIR__.'/../../views/console/database/search/files.phtml');
|
||||
$searchDocuments = new View(__DIR__.'/../../views/console/database/search/documents.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', false)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('searchFiles', $searchFiles)
|
||||
->setParam('searchDocuments', $searchDocuments)
|
||||
->setParam('logs', $logs)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Document')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/database/document/new')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', true)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('logs', new View())
|
||||
;
|
||||
|
||||
$layout
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ $cli
|
|||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if($attempts >= $max) {
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
|
|
@ -277,17 +277,20 @@ $cli
|
|||
$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 . '\'';
|
||||
return "\"{$filter}\"='{$value}'";
|
||||
}, array_keys($filters), array_values($filters)));
|
||||
} else {
|
||||
$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)');
|
||||
$query = "SELECT sum(value) AS \"value\" FROM \"{$table}\" WHERE \"time\" > '{$start}' AND \"time\" < '{$end}' AND \"metric_type\"='counter' {$filters} GROUP BY time({$period['key']}), \"projectId\" {$groupBy} FILL(null)";
|
||||
$result = $database->query($query);
|
||||
|
||||
$points = $result->getPoints();
|
||||
foreach ($points as $point) {
|
||||
$projectId = $point['projectId'];
|
||||
|
||||
if (!empty($projectId) && $projectId != 'console') {
|
||||
if (!empty($projectId) && $projectId !== 'console') {
|
||||
$dbForProject->setNamespace('project_' . $projectId . '_internal');
|
||||
$metricUpdated = $metric;
|
||||
|
||||
|
|
@ -315,8 +318,11 @@ $cli
|
|||
'type' => 0,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $value));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $value)
|
||||
);
|
||||
}
|
||||
$latestTime[$metric][$period['key']] = $time;
|
||||
} catch (\Exception $e) { // if projects are deleted this might fail
|
||||
|
|
@ -332,7 +338,7 @@ $cli
|
|||
* 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
|
||||
if ($iterations % 30 === 0) { // Every 15 minutes aggregate number of objects in database
|
||||
|
||||
$latestProject = null;
|
||||
|
||||
|
|
@ -344,7 +350,7 @@ $cli
|
|||
do { // list projects
|
||||
try {
|
||||
$attempts++;
|
||||
$projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject);
|
||||
$projects = $dbForConsole->find('projects', [], 100, cursor: $latestProject);
|
||||
break; // leave the do-while if successful
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
|
||||
|
|
@ -381,8 +387,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $storageTotal));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -398,8 +407,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $storageTotal));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$collections = [
|
||||
|
|
@ -442,8 +454,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -459,8 +474,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$subCollections = $options['subCollections'] ?? [];
|
||||
|
|
@ -474,7 +492,7 @@ $cli
|
|||
|
||||
do { // Loop over all the parent collection document for each sub collection
|
||||
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}");
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
|
||||
if (empty($parents)) {
|
||||
continue;
|
||||
|
|
@ -505,8 +523,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -522,8 +543,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -550,8 +574,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -567,11 +594,14 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (\Exception$e) {
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@
|
|||
data-name="securityLogs"
|
||||
data-event="load">
|
||||
|
||||
<div class="box">
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical small">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -330,6 +330,33 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="account.getLogs"
|
||||
data-event="submit"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-scope="console"
|
||||
data-name="securityLogs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{securityLogs.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{securityLogs.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="account.getLogs"
|
||||
data-event="submit"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-scope="console"
|
||||
data-name="securityLogs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{securityLogs.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -211,8 +211,9 @@
|
|||
required
|
||||
maxlength="36"
|
||||
class=""
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="projectId" />
|
||||
|
||||
|
||||
<label>Name</label>
|
||||
<input type="text" class="full-width margin-bottom-xl" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
|
|
|
|||
|
|
@ -1,78 +1,96 @@
|
|||
<?php
|
||||
$interval = floor((int)$this->getParam('interval', 0) / 86400);
|
||||
$method = $this->getParam('method', '');
|
||||
$params = $this->getParam('params', []);
|
||||
?>
|
||||
<div
|
||||
data-service="database.listCollectionLogs"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-service="<?php echo $method; ?>"
|
||||
<?php foreach($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-scope="sdk"
|
||||
data-event="load"
|
||||
data-name="project-collection-logs">
|
||||
|
||||
<div class="box margin-bottom">
|
||||
<div data-ls-if="0 == {{project-collection-logs.logs.length}}">
|
||||
data-name="logs">
|
||||
<div data-ls-if="0 == {{logs.logs.length}}">
|
||||
<div class="box margin-bottom">
|
||||
<h3 class="margin-bottom-small text-bold">No Logs Found</h3>
|
||||
|
||||
<p class="margin-bottom-no">Logs are retained for <?php echo $this->escape($interval); ?> days.</p>
|
||||
</div>
|
||||
|
||||
<table class="vertical small" data-ls-if="0 != {{project-collection-logs.logs.length}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="120">Date</th>
|
||||
<th width="180">Initiator</th>
|
||||
<th>Event</th>
|
||||
<th width="110">Location</th>
|
||||
<th width="90">IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="project-collection-logs.logs" data-ls-as="log" class="text-size-small">
|
||||
<tr>
|
||||
<td data-title="Date: "><span class="text-fade" data-ls-bind="{{log.time|dateTime}}"></span></td>
|
||||
<td data-title="Initiator: ">
|
||||
<span data-ls-if="{{log.userName|escape}} !== '' && {{log.mode}} === ''"><i class="icon-user"></i> <a data-ls-attrs="href=/console/users/user?id={{log.userId}}&project={{router.params.project}}" data-ls-bind="{{log.userName}}"></a></span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} !== '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Unknown</span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} === '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Anonymous User</span>
|
||||
<span data-ls-if="{{log.mode}} === 'admin'">
|
||||
<img src="" data-ls-attrs="src={{log.userName|avatar}}" data-size="45" alt="User Avatar" class="avatar xxs inline margin-end-small" loading="lazy" width="30" height="30" /> <span data-ls-bind="{{log.userName}}"></span> <span class="text-fade text-size-xs">(Admin)</span>
|
||||
</span>
|
||||
<span data-ls-if="{{log.mode}} === 'key'"> <i class="icon-key"></i> API Key</span>
|
||||
</td>
|
||||
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
|
||||
<td data-title="Location: ">
|
||||
<img onerror="this.onerror=null;this.className='avatar xxs hide'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
|
||||
<span data-ls-bind="{{log.countryName}}"></span>
|
||||
</td>
|
||||
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="database.listCollectionLogs"
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-collection-logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-collection-logs.sum}}" class="margin-end-small round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
<div data-ls-if="0 != {{logs.sum}}">
|
||||
<div class="margin-bottom-small margin-top-negative text-align-end text-size-small text-fade">Showing logs from the last <?php echo $this->escape($interval); ?> days</div>
|
||||
|
||||
<form
|
||||
data-service="database.listCollectionLogs"
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-collection-logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-collection-logs.sum}}" class="margin-start-small round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="120">Date</th>
|
||||
<th width="180">By</th>
|
||||
<th>Event</th>
|
||||
<th width="110">Location</th>
|
||||
<th width="90">IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="logs.logs" data-ls-as="log" class="text-size-small">
|
||||
<tr>
|
||||
<td data-title="Date: "><span class="text-fade" data-ls-bind="{{log.time|dateTime}}"></span></td>
|
||||
<td data-title="By: ">
|
||||
<span data-ls-if="{{log.userName|escape}} !== '' && {{log.mode}} === ''"><i class="icon-user"></i> <a data-ls-attrs="href=/console/users/user?id={{log.userId}}&project={{router.params.project}}" data-ls-bind="{{log.userName}}"></a></span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} !== '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Unknown</span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} === '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Anonymous User</span>
|
||||
<span data-ls-if="{{log.mode}} === 'admin'">
|
||||
<img src="" data-ls-attrs="src={{log.userName|avatar}}" data-size="45" alt="User Avatar" class="avatar xxs inline margin-end-small" loading="lazy" width="30" height="30" /> <span data-ls-bind="{{log.userName}}"></span> <span class="text-fade text-size-xs">(Admin)</span>
|
||||
</span>
|
||||
<span data-ls-if="{{log.mode}} === 'key'"> <i class="icon-key"></i> API Key</span>
|
||||
</td>
|
||||
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
|
||||
<td data-title="Location: ">
|
||||
<img onerror="this.onerror=null;this.className='avatar xxs hide'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
|
||||
<span data-ls-bind="{{log.countryName}}"></span>
|
||||
</td>
|
||||
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="<?php echo $method; ?>"
|
||||
<?php foreach($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{logs.sum}}" class="margin-end-small round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{logs.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="<?php echo $method; ?>"
|
||||
<?php foreach($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{logs.sum}}" class="margin-start-small round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,16 @@
|
|||
<?php
|
||||
|
||||
$new = $this->getParam('new', false);
|
||||
$logs = $this->getParam('logs', null);
|
||||
|
||||
?>
|
||||
<div
|
||||
data-service="database.getCollection"
|
||||
data-param-collection-id="{{router.params.collection}}"
|
||||
data-scope="sdk"
|
||||
data-event="load,database.updateDocument"
|
||||
data-name="project-collection">
|
||||
|
||||
|
||||
<div
|
||||
data-service="database.getDocument"
|
||||
data-param-collection-id="{{router.params.collection}}"
|
||||
|
|
@ -27,7 +33,7 @@
|
|||
|
||||
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
||||
<h2>JSON View</h2>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
|
@ -40,9 +46,9 @@
|
|||
<div class="zone xl margin-bottom-no">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/database/document?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
|
||||
<h2>Overview</h2>
|
||||
<h2 class="margin-bottom">Overview</h2>
|
||||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="row responsive">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form
|
||||
data-analytics
|
||||
|
|
@ -54,28 +60,74 @@
|
|||
data-name="project-document"
|
||||
data-scope="sdk"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Updated document successfully"
|
||||
data-success="trigger,redirect"
|
||||
data-success-param-trigger-events="database.updateDocument"
|
||||
data-success-param-redirect-url="/console/database/document?id={{serviceData.$id}}&collection={{project-collection.$id}}&project={{router.params.project}}"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update document"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
|
||||
<input type="hidden" name="documentId" data-ls-bind="{{project-document.$id}}" />
|
||||
|
||||
<label> </label>
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
|
||||
<?php if(!$new): ?><input type="hidden" name="documentId" data-ls-bind="{{project-document.$id}}" /><?php endif; ?>
|
||||
|
||||
<div class="box">
|
||||
<fieldset name="data" data-cast-to="object">
|
||||
<?php if($new): ?>
|
||||
<label for="documentId">Document ID</label>
|
||||
<input
|
||||
type="hidden"
|
||||
data-custom-id
|
||||
data-id-type="auto"
|
||||
data-validator="database.getDocument"
|
||||
required
|
||||
maxlength="36"
|
||||
name="documentId"
|
||||
id="documentId"
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<?php endif; ?>
|
||||
|
||||
<fieldset name="data" data-cast-to="object">
|
||||
<ul data-ls-loop="project-collection.attributes" data-ls-as="attribute">
|
||||
<li>
|
||||
<li data-ls-if="{{attribute.status}} === 'available'">
|
||||
<label>
|
||||
<div data-ls-bind="{{attribute.key}}" class="margin-bottom-tiny"></div>
|
||||
<div data-ls-if="{{attribute.required}}" class="text-size-xs text-danger text-fade">required</div>
|
||||
<div data-ls-if="!{{attribute.required}}" class="text-size-xs text-fade">optional</div>
|
||||
</label>
|
||||
<textarea data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{project-document|documentAttribute}}"></textarea>
|
||||
<div data-ls-if="!{{attribute.array}}">
|
||||
<div data-ls-if="{{attribute.format}}" data-ls-template="template-{{attribute.format}}" data-type="script"></div>
|
||||
<div data-ls-if="!{{attribute.format}}" data-ls-template="template-{{attribute.type}}" data-type="script"></div>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="{{attribute.array}}">
|
||||
<input type="hidden" data-forms-required="{{attribute.required}}" data-ls-attrs="name={{attribute.key}}" data-cast-to="array-empty">
|
||||
<div data-ls-loop="project-document.{{attribute.key}}" data-ls-as="item">
|
||||
<div class="row responsive thin margin-bottom-tiny" data-forms-remove>
|
||||
<div class="col span-11 margin-bottom-small">
|
||||
<div data-ls-if="{{attribute.format}}" data-ls-template="template-{{attribute.format}}-array" data-type="script"></div>
|
||||
<div data-ls-if="!{{attribute.format}}" data-ls-template="template-{{attribute.type}}-array" data-type="script"></div>
|
||||
</div>
|
||||
<div class="col span-1 margin-bottom-small">
|
||||
<button type="button" data-remove class="dark danger small round pull-end" style="margin-top: 10px;"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-ls-attrs="id=attribute-{{attribute.key}}"></div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<div data-forms-clone data-label="Add Attribute" data-target="attribute-{{attribute.key}}" data-first="0">
|
||||
<div class="row responsive thin margin-bottom-tiny" data-forms-remove>
|
||||
<div class="col span-11 margin-bottom-small">
|
||||
<div data-ls-if="{{attribute.format}}" data-ls-template="template-{{attribute.format}}-array" data-type="script"></div>
|
||||
<div data-ls-if="!{{attribute.format}}" data-ls-template="template-{{attribute.type}}-array" data-type="script"></div>
|
||||
</div>
|
||||
<div class="col span-1 margin-bottom-small">
|
||||
<button type="button" data-remove class="dark danger small round pull-end" style="margin-top: 10px;"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
|
|
@ -145,10 +197,82 @@
|
|||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- <li data-ls-if="{{project-document.$id}}" data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
|
||||
<h2>Activity</h2>
|
||||
</li> -->
|
||||
<?php if(!$new): ?>
|
||||
<li data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
|
||||
<h2>Activity <span class="badge" data-ls-bind="{{logs.sum}}"></span></h2>
|
||||
|
||||
<?php echo $logs->render(); ?>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="template-string">
|
||||
<textarea data-forms-text-resize data-forms-text-direction data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string"></textarea>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-string-array">
|
||||
<textarea data-forms-text-resize data-forms-text-direction data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string"></textarea>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-integer">
|
||||
<input type="number" step="1" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="integer" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-integer-array">
|
||||
<input type="number" step="1" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="integer" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-double">
|
||||
<input type="number" step="0.01" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="float" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-double-array">
|
||||
<input type="number" step="0.01" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="float" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-boolean">
|
||||
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="boolean" class="margin-bottom-no" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-boolean-array">
|
||||
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="boolean" class="margin-bottom-no" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-url">
|
||||
<input type="url" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-url-array">
|
||||
<input type="url" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-email">
|
||||
<input type="email" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-email-array">
|
||||
<input type="email" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-ip">
|
||||
<input type="text" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string" pattern="((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))" title="Enter a valid IPV4 or IPV6 address" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-ip-array">
|
||||
<input type="text" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string" pattern="((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))" title="Enter a valid IPV4 or IPV6 address" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-enum">
|
||||
<select data-ls-attrs="name={{attribute.key}}" data-ls-loop="attribute.elements" data-ls-as="element" data-cast-to="string">
|
||||
<option data-ls-attrs="value={{element}}" data-ls-bind="{{element}}" data-forms-selected="{{project-document|documentAttribute}}"></option>
|
||||
</select>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-enum-array">
|
||||
<select data-ls-attrs="name={{attribute.key}}" data-ls-loop="attribute.elements" data-ls-as="element" data-cast-to="string">
|
||||
<option data-ls-attrs="value={{element}}" data-ls-bind="{{element}}" data-forms-selected="{{item}}"></option>
|
||||
</select>
|
||||
</script>
|
||||
|
|
@ -102,6 +102,7 @@
|
|||
data-validator="database.getCollection"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="collectionId" />
|
||||
|
||||
<label for="collection-name">Name</label>
|
||||
|
|
@ -118,9 +119,110 @@
|
|||
</div>
|
||||
|
||||
</li>
|
||||
<!-- <li data-state="/console/database/usage?project={{router.params.project}}">
|
||||
<li data-state="/console/database/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="database.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="database.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="database.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
</li> -->
|
||||
|
||||
<div
|
||||
data-service="database.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<h3 class="margin-bottom-tiny">Objects</h3>
|
||||
<p class="text-fade">Count of collections and documents over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Collections=collectionsCount,Documents=documentsCount"
|
||||
data-show-y-axis="true"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Collections <span data-ls-bind="({{usage.collectionsCount|statsGetLast|statsTotal}})"></span></li>
|
||||
<li>Total Documents <span data-ls-bind="({{usage.documentsCount|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 class="margin-bottom-tiny">Collections</h3>
|
||||
<p class="text-fade">Count of collections create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=collectionsCreate,Read=collectionsRead,Updated=collectionsUpdate,Deleted=collectionsDelete"
|
||||
data-show-y-axis="true"
|
||||
data-colors="create,read,update,delete"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large crud">
|
||||
<li>Created</li>
|
||||
<li>Read</li>
|
||||
<li>Updated</li>
|
||||
<li>Deleted</li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Documents</h3>
|
||||
<p class="text-fade">Count of documents create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=documentsCreate,Read=documentsRead,Updated=documentsUpdate,Deleted=documentsDelete"
|
||||
data-show-y-axis="true"
|
||||
data-colors="create,read,update,delete"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large crud">
|
||||
<li>Created</li>
|
||||
<li>Read</li>
|
||||
<li>Updated</li>
|
||||
<li>Deleted</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -247,37 +247,37 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Executions=usage.executions.data" data-height="140" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Executions=functionsExecutions" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Executions <span data-ls-bind="({{usage.executions.total}})"></span></li>
|
||||
<li>Executions <span data-ls-bind="({{usage.functionsExecutions|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (seconds)=usage.compute.data" data-colors="orange" data-height="140" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (seconds)=functionsCompute" data-colors="orange" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li class="orange">CPU Time <span data-ls-bind="({{usage.compute.total|seconds2hum}})"></span></li>
|
||||
<li class="orange">CPU Time <span data-ls-bind="({{usage.functionsCompute|statsGetLast|seconds2hum}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Failures=usage.failures.data" data-colors="red" data-height="140" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Failures=functionsFailures" data-colors="red" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li class="red">Errors <span data-ls-bind="({{usage.failures.total}})"></span></li>
|
||||
<li class="red">Errors <span data-ls-bind="({{usage.functionsFailures|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ $runtimes = $this->getParam('runtimes', []);
|
|||
data-validator="functions.get"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="functionId" />
|
||||
|
||||
<label for="name">Name</label>
|
||||
|
|
@ -125,7 +126,7 @@ $runtimes = $this->getParam('runtimes', []);
|
|||
<?php endforeach;?>
|
||||
</select>
|
||||
|
||||
<input id="execute" name="execute" value="" hidden/>
|
||||
<input id="execute" name="execute" value="" hidden />
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
|
|
|
|||
|
|
@ -93,11 +93,11 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<div class="row responsive">
|
||||
<div class="col span-9">
|
||||
<div class="chart pull-end">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Requests=usage.requests.data" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Requests=requests" />
|
||||
</div>
|
||||
|
||||
<div class="chart-metric">
|
||||
<div class="value margin-bottom-small"><span class="sum" data-ls-bind="{{usage.requests.total|statsTotal}}">N/A</span></div>
|
||||
<div class="value margin-bottom-small"><span class="sum" data-ls-bind="{{usage.requests|statsGetLast|statsTotal}}">N/A</span></div>
|
||||
<div class="unit margin-start-no margin-bottom-small">Requests</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -115,22 +115,22 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<div class="box dashboard">
|
||||
<div class="row responsive">
|
||||
<div class="col span-3">
|
||||
<div class="value"><span class="sum" data-ls-bind="{{usage.documents.total|statsTotal}}" data-default="0">0</span></div>
|
||||
<div class="value"><span class="sum" data-ls-bind="{{usage.documents|statsGetLast|statsTotal}}" data-default="0">0</span></div>
|
||||
<div class="margin-top-small"><b class="text-size-small unit">Documents</b></div>
|
||||
</div>
|
||||
<div class="col span-3">
|
||||
<div class="value">
|
||||
<span class="sum" data-ls-bind="{{usage.storage.total|humanFileSize}}" data-default="0">0</span>
|
||||
<span data-ls-bind="{{usage.storage.total|humanFileUnit}}" class="text-size-small unit"></span>
|
||||
<span class="sum" data-ls-bind="{{usage.storage|statsGetLast|humanFileSize}}" data-default="0">0</span>
|
||||
<span data-ls-bind="{{usage.storage|statsGetLast|humanFileUnit}}" class="text-size-small unit"></span>
|
||||
</div>
|
||||
<div class="margin-top-small"><b class="text-size-small unit">Storage</b></div>
|
||||
</div>
|
||||
<div class="col span-3">
|
||||
<div class="value"><span class="sum" data-ls-bind="{{usage.users.total}}" data-default="0">0</span></div>
|
||||
<div class="value"><span class="sum" data-ls-bind="{{usage.users|statsGetLast|statsTotal}}" data-default="0">0</span></div>
|
||||
<div class="margin-top-small"><b class="text-size-small unit">Users</b></div>
|
||||
</div>
|
||||
<div class="col span-3">
|
||||
<div class="value"><span class="sum" data-ls-bind="{{usage.functions.total|statsTotal}}" data-default="0">0</span></div>
|
||||
<div class="value"><span class="sum" data-ls-bind="{{usage.functions|statsGetLast|statsTotal}}" data-default="0">0</span></div>
|
||||
<div class="margin-top-small"><b class="text-size-small unit">Executions</b></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ $home = $this->getParam('home', '');
|
|||
<li class="margin-bottom">
|
||||
<a data-ls-attrs="href=/console/home?project={{project.$id}}" class="box">
|
||||
<div data-ls-bind="{{project.name}}" class="text-one-liner margin-bottom-tiny text-bold"> </div>
|
||||
|
||||
|
||||
<p data-ls-if="({{project.platforms.length}})" class="text-fade text-size-small" data-ls-bind="{{project.platforms.length}} apps"></p>
|
||||
<p data-ls-if="(!{{project.platforms.length}})" class="text-fade text-size-small"> </p>
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ $home = $this->getParam('home', '');
|
|||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{console-projects.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<button data-ls-ui-trigger="create-project">Create Project</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -89,9 +89,9 @@ $home = $this->getParam('home', '');
|
|||
<div class="col span-6 margin-bottom">
|
||||
<div class="box line community">
|
||||
<h3 class="margin-bottom-small text-size-normal">Join The Community</h3>
|
||||
|
||||
|
||||
<p class="text-fade">Join Appwrite growing developers community channels.</p>
|
||||
|
||||
|
||||
<a href="<?php echo APP_SOCIAL_TWITTER; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> on Twitter"
|
||||
data-analytics
|
||||
data-analytics-event="click"
|
||||
|
|
@ -122,7 +122,7 @@ $home = $this->getParam('home', '');
|
|||
<div class="col span-6 margin-bottom">
|
||||
<div class="box line">
|
||||
<h3 class="margin-bottom-small text-size-normal">Read The Docs</h3>
|
||||
|
||||
|
||||
<p class="text-fade">Take full advantage of Appwrite APIs and tools for your new project.</p>
|
||||
|
||||
<a data-ls-attrs="href={{env.HOME}}/docs" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Full Documentation</a>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<!-- <div data-ls-if="0 !== {{console-domains|activeDomainsCount}}">
|
||||
<label for="name">Custom API Endpoints</label>
|
||||
|
||||
|
||||
<ul data-ls-loop="console-domains" data-ls-as="domain">
|
||||
<li>
|
||||
<div class="input-copy" data-ls-if="true === {{domain.verification}} && {{domain.certificateId}}">
|
||||
|
|
@ -73,7 +73,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
<button class="" type="submit">Update</button>
|
||||
</form>
|
||||
|
|
@ -110,14 +110,14 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<div data-ui-modal class="modal box close width-small height-small" data-button-text="Delete Project" data-button-class="danger reverse">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
||||
<h3>Confirm Password</h3>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<label>Password</label>
|
||||
<input name="password" type="password" class="full-width" autocomplete="off" placeholder="" required>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<button type="submit" class="margin-bottom-no danger fill">Delete Project</button>
|
||||
|
|
@ -232,7 +232,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</form>
|
||||
|
||||
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="" class="pull-start provider margin-end" />
|
||||
|
||||
|
||||
<span class="text-size-small text-bold"><?php echo $this->escape($name); ?></span>
|
||||
|
||||
<?php if($docs): ?>
|
||||
|
|
@ -241,7 +241,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="text-align-center text-size-small text-bold text-success" data-ls-if="!!({{console-project.serviceStatusFor<?php echo ucFirst($this->escape($key)); ?>}})">Enabled</div>
|
||||
|
|
@ -252,15 +252,15 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</ul>
|
||||
</li>
|
||||
<li data-state="/console/settings/domains?project={{router.params.project}}">
|
||||
|
||||
|
||||
<?php if(!$customDomainsEnabled): ?>
|
||||
<h2 style="display: none;">Custom Domains</h2>
|
||||
|
||||
<div class="box line margin-bottom">
|
||||
<h3>Enable Custom Domains</h3>
|
||||
|
||||
|
||||
<p>To enable <?php echo APP_NAME; ?>'s custom domain feature, you have to start your server instance with a public accessible domain name.</p>
|
||||
|
||||
|
||||
<p>Start your <?php echo APP_NAME; ?> server container with the <b>_APP_DOMAIN_TARGET</b> environment variable set with a public accessible domain name that resolves to your <?php echo APP_NAME; ?> server setup.</p>
|
||||
<p class="margin-bottom-no">The <?php echo APP_NAME; ?> server will use your target domain to validate new custom domains and will automatically generate SSL certificates for your new domains using Let'sencrypt Certbot.</p>
|
||||
</div>
|
||||
|
|
@ -312,7 +312,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<td data-title="">
|
||||
<button class="link text-size-small" data-ls-if="true === {{domain.verification}}" data-ls-ui-trigger="dns-settings-{{domain.$id}}">DNS Settings</button>
|
||||
<button class="link text-size-small" data-ls-if="true !== {{domain.verification}}" data-ls-ui-trigger="dns-settings-{{domain.$id}}">Verify Domain</button>
|
||||
|
||||
|
||||
<div data-ui-modal class="modal box close" data-button-alias="none" data-open-event="dns-settings-{{domain.$id}}" xdata-close-event="dns-settings-close-{{domain.$id}}">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
|
@ -326,7 +326,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<ol class="bullets">
|
||||
<li>
|
||||
<p>Add a new CNAME record in your DNS providers settings to point your new subdomain to your <?php echo APP_NAME; ?> server with the following value:</p>
|
||||
|
||||
|
||||
<div class="ide margin-bottom-small">
|
||||
<pre class="line-numbers"><code class="prism language-javascript" data-prism><?php echo $this->print($customDomainsTarget, 'escape'); ?></code></pre>
|
||||
</div>
|
||||
|
|
@ -359,7 +359,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="domainId" data-ls-bind="{{domain.$id}}" />
|
||||
|
||||
|
||||
<button class="margin-top-small">Confirm & Verify</button>
|
||||
</form>
|
||||
</li>
|
||||
|
|
@ -428,7 +428,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<label for="name">Domain Name</label>
|
||||
<input type="text" class="full-width" id="domain" name="domain" placeholder="appwrite.example.com" required autocomplete="off" title="Enter a valid domain name" pattern="^([a-zA-Z0-9][a-zA-Z0-9-_]*\.)*[a-zA-Z0-9]*[a-zA-Z0-9-_]*[[a-zA-Z0-9]+$" />
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
|
|
@ -463,6 +463,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-scope="console"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger"
|
||||
data-confirm="Are you sure you want to remove that user from the team?"
|
||||
data-success-param-alert-text="Member Removed Successfully"
|
||||
data-success-param-trigger-events="teams.deleteMembership"
|
||||
data-failure="alert"
|
||||
|
|
@ -474,7 +475,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<button class="danger">Leave</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div data-ls-if="false === {{member.confirm}}" class="pull-end margin-end">
|
||||
<form class="pull-end"
|
||||
data-analytics
|
||||
|
|
@ -553,7 +554,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<label for="email">Email</label>
|
||||
<input name="email" id="email" type="email" autocomplete="email" required>
|
||||
|
||||
|
||||
<label for="team-name">Name <small>(optional)</small></label>
|
||||
<input name="name" id="team-name" type="text" autocomplete="name" maxlength="128" />
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
|
||||
<div class="zone xl">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/database?project={{router.params.project}}">
|
||||
<li data-state="/console/storage?project={{router.params.project}}">
|
||||
<h2 class="margin-bottom">Files</h2>
|
||||
|
||||
<form class="box padding-small margin-bottom search"
|
||||
|
|
@ -245,6 +245,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
data-validator="storage.getFile"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="fileId"
|
||||
id="fileId" />
|
||||
|
||||
|
|
@ -268,5 +269,66 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/storage/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="storage.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div
|
||||
data-service="storage.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Total Files=files" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Files <span data-ls-bind="({{usage.files|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Total Storage=storage" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Storage (<span data-ls-bind="{{usage.storage|statsGetLast|humanFileSize}}"></span> <span data-ls-bind="{{usage.storage|statsGetLast|humanFileUnit}}"></span>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-validator="users.get"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
id="userId"
|
||||
name="userId" />
|
||||
|
||||
|
|
@ -310,6 +311,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-validator="teams.get"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
id="teamId"
|
||||
name="teamId" />
|
||||
|
||||
|
|
@ -327,7 +329,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<li data-state="/console/users/providers?project={{router.params.project}}">
|
||||
<p data-ls-if="{{console-project.authLimit}} == 0" class="text-fade text-size-small margin-bottom pull-end">Unlimited Users <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Set Limit</a></p>
|
||||
<p data-ls-if="{{console-project.authLimit}} != 0" class="text-fade text-size-small margin-bottom pull-end"><span data-ls-bind="{{console-project.authLimit|statsTotal}}"></span> Users allowed <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Change Limit</a></p>
|
||||
|
||||
|
||||
<h2>Settings</h2>
|
||||
|
||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="project-update-auth-users-limit">
|
||||
|
|
@ -335,8 +337,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<h1>Max Allowed Users</h1>
|
||||
|
||||
<form data-debug="1"
|
||||
data-analytics
|
||||
<form data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
|
|
@ -369,7 +370,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<ul class="tiles cell-3 margin-bottom-small">
|
||||
<?php foreach($auth as $index => $method):
|
||||
$key = $method['key'] ?? '';
|
||||
$key = ucfirst($method['key'] ?? '');
|
||||
$name = $method['name'] ?? '';
|
||||
$icon = $method['icon'] ?? '';
|
||||
$docs = $method['docs'] ?? '';
|
||||
|
|
@ -394,17 +395,17 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update project auth status settings"
|
||||
data-failure-param-alert-classname="error">
|
||||
<input name="method" id="<?php echo $this->escape($key); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($index); ?>">
|
||||
<input name="method" id="auth<?php echo $this->escape($key); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($index); ?>">
|
||||
<?php if( !(in_array($key, ['usersAuthMagicURL', 'usersAuthInvites']) && !$smtpEnabled)): ?>
|
||||
<input name="status" type="hidden" data-forms-switch data-ls-bind="{{console-project.<?php echo $this->escape($key); ?>}}" data-cast-to="boolean" class="pull-end" />
|
||||
<input name="status" type="hidden" data-forms-switch data-ls-bind="{{console-project.auth<?php echo $this->escape($key); ?>}}" data-cast-to="boolean" class="pull-end" />
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="Email/Password Logo" class="pull-start provider margin-end" />
|
||||
|
||||
|
||||
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <spann class="text-fade text-size-xs">soon</span><?php endif; ?>
|
||||
|
||||
|
||||
<?php if( in_array($key, ['usersAuthMagicURL', 'usersAuthInvites']) && !$smtpEnabled): ?>
|
||||
<p class="margin-bottom-no text-one-liner text-size-small text-danger">
|
||||
SMTP Disabled
|
||||
|
|
@ -431,18 +432,18 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-scope="console">
|
||||
<ul class="tiles cell-3 margin-bottom-small">
|
||||
<?php foreach ($providers as $provider => $data):
|
||||
if (isset($data['enabled']) && !$data['enabled']) {continue;}
|
||||
// if (isset($data['mock']) && $data['mock']) {continue;}
|
||||
if (isset($data['enabled']) && !$data['enabled']) {continue;}
|
||||
if (isset($data['mock']) && $data['mock']) {continue;}
|
||||
$sandbox = $data['sandbox'] ?? false;
|
||||
$form = $data['form'] ?? false;
|
||||
$name = $data['name'] ?? 'Unknown';
|
||||
$beta = $data['beta'] ?? false;
|
||||
?>
|
||||
<li class="<?php echo (isset($data['enabled']) && !$data['enabled']) ? 'dev-feature' : ''; ?>">
|
||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
?>
|
||||
<li class="<?php echo (isset($data['enabled']) && !$data['enabled']) ? 'dev-feature' : ''; ?>">
|
||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1><?php echo $this->escape($name); ?> <?php if ($sandbox): ?>Sandbox<?php endif;?> OAuth2 Settings</h1>
|
||||
<h1><?php echo $this->escape($name); ?> <?php if ($sandbox): ?>Sandbox<?php endif;?> OAuth2 Settings</h1>
|
||||
|
||||
<form
|
||||
autocomplete="off"
|
||||
|
|
@ -470,7 +471,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<?php if (!$form): ?>
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">App ID</label>
|
||||
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}">
|
||||
|
||||
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
|
||||
<input name="secret" data-forms-show-secret id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="password" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
|
||||
<?php else: ?>
|
||||
|
|
@ -524,5 +525,79 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/users/usage?project={{router.params.project}}">
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="users.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="users.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="users.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div data-service="users.getUsage" data-event="load" data-name="usage">
|
||||
<h3 class="margin-bottom-tiny">Users</h3>
|
||||
<p class="text-fade">Count of users over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Users=usersCount" data-show-y-axis="true" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Users <span data-ls-bind="({{usage.usersCount|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Operations</h3>
|
||||
<p class="text-fade">Count of users create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=usersCreate,Read=usersRead,Updated=usersUpdate,Deleted=usersDelete"
|
||||
data-show-y-axis="true"
|
||||
data-colors="create,read,update,delete"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large crud">
|
||||
<li>Created</li>
|
||||
<li>Read</li>
|
||||
<li>Updated</li>
|
||||
<li>Deleted</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@
|
|||
data-service="teams.deleteMembership"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger"
|
||||
data-confirm="Are you sure you want to remove that user from the team?"
|
||||
data-success-param-alert-text="Member Removed Successfully"
|
||||
data-success-param-trigger-events="teams.deleteMembership"
|
||||
data-failure="alert"
|
||||
|
|
|
|||
|
|
@ -106,12 +106,18 @@ class DatabaseV1 extends Worker
|
|||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
$collectionId = $collection->getId();
|
||||
$key = $attribute->getAttribute('key', '');
|
||||
$status = $attribute->getAttribute('status', '');
|
||||
|
||||
// possible states at this point:
|
||||
// - available: should not land in queue; controller flips these to 'deleting'
|
||||
// - processing: hasn't finished creating
|
||||
// - deleting: was available, in deletion queue for first time
|
||||
// - failed: attribute was never created
|
||||
// - stuck: attribute was available but cannot be removed
|
||||
try {
|
||||
if(!$dbForExternal->deleteAttribute($collectionId, $key) && $attribute->getAttribute('status') !== 'failed') {
|
||||
if($status !== 'failed' && !$dbForExternal->deleteAttribute($collectionId, $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
}
|
||||
|
||||
$dbForInternal->deleteDocument('attributes', $attribute->getId());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
|
|
@ -214,12 +220,12 @@ class DatabaseV1 extends Worker
|
|||
|
||||
$collectionId = $collection->getId();
|
||||
$key = $index->getAttribute('key');
|
||||
$status = $index->getAttribute('status', '');
|
||||
|
||||
try {
|
||||
if(!$dbForExternal->deleteIndex($collectionId, $key) && $index->getAttribute('status') !== 'failed') {
|
||||
if($status !== 'failed' && !$dbForExternal->deleteIndex($collectionId, $key)) {
|
||||
throw new Exception('Failed to delete index');
|
||||
}
|
||||
|
||||
$dbForInternal->deleteDocument('indexes', $index->getId());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ services:
|
|||
- ./docs:/usr/src/code/docs
|
||||
- ./public:/usr/src/code/public
|
||||
- ./src:/usr/src/code/src
|
||||
- ./debug:/tmp
|
||||
# - ./debug:/tmp
|
||||
- ./dev:/usr/local/dev
|
||||
depends_on:
|
||||
- mariadb
|
||||
|
|
|
|||
1
docs/references/database/get-document-logs.md
Normal file
1
docs/references/database/get-document-logs.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Get the document activity logs list by its unique ID.
|
||||
|
|
@ -13,6 +13,7 @@ const configApp = {
|
|||
mainFile: 'app.js',
|
||||
src: [
|
||||
'public/scripts/dependencies/litespeed.js',
|
||||
'public/scripts/dependencies/alpine.js',
|
||||
|
||||
'public/scripts/init.js',
|
||||
|
||||
|
|
@ -59,9 +60,11 @@ const configApp = {
|
|||
'public/scripts/views/forms/oauth-apple.js',
|
||||
'public/scripts/views/forms/password-meter.js',
|
||||
'public/scripts/views/forms/pell.js',
|
||||
'public/scripts/views/forms/required.js',
|
||||
'public/scripts/views/forms/remove.js',
|
||||
'public/scripts/views/forms/run.js',
|
||||
'public/scripts/views/forms/select-all.js',
|
||||
'public/scripts/views/forms/selected.js',
|
||||
'public/scripts/views/forms/show-secret.js',
|
||||
'public/scripts/views/forms/switch.js',
|
||||
'public/scripts/views/forms/tags.js',
|
||||
|
|
|
|||
412
public/dist/scripts/app-all.js
vendored
412
public/dist/scripts/app-all.js
vendored
File diff suppressed because one or more lines are too long
24
public/dist/scripts/app-dep.js
vendored
24
public/dist/scripts/app-dep.js
vendored
|
|
@ -5,7 +5,7 @@ function rejected(value){try{step(generator["throw"](value));}catch(e){reject(e)
|
|||
function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected);}
|
||||
step((generator=generator.apply(thisArg,_arguments||[])).next());});}
|
||||
class AppwriteException extends Error{constructor(message,code=0,response=''){super(message);this.name='AppwriteException';this.message=message;this.code=code;this.response=response;}}
|
||||
class Appwrite{constructor(){this.config={endpoint:'https://appwrite.io/v1',endpointRealtime:'',project:'',key:'',jwt:'',locale:'',mode:'',};this.headers={'x-sdk-version':'appwrite:web:4.0.4','X-Appwrite-Response-Format':'0.12.0',};this.realtime={socket:undefined,timeout:undefined,url:'',channels:new Set(),subscriptions:new Map(),subscriptionsCounter:0,reconnect:true,reconnectAttempts:0,lastMessage:undefined,connect:()=>{clearTimeout(this.realtime.timeout);this.realtime.timeout=window===null||window===void 0?void 0:window.setTimeout(()=>{this.realtime.createSocket();},50);},getTimeout:()=>{switch(true){case this.realtime.reconnectAttempts<5:return 1000;case this.realtime.reconnectAttempts<15:return 5000;case this.realtime.reconnectAttempts<100:return 10000;default:return 60000;}},createSocket:()=>{var _a,_b;if(this.realtime.channels.size<1)
|
||||
class Appwrite{constructor(){this.config={endpoint:'https://appwrite.io/v1',endpointRealtime:'',project:'',key:'',jwt:'',locale:'',mode:'',};this.headers={'x-sdk-version':'appwrite:web:4.0.4','X-Appwrite-Response-Format':'0.11.0',};this.realtime={socket:undefined,timeout:undefined,url:'',channels:new Set(),subscriptions:new Map(),subscriptionsCounter:0,reconnect:true,reconnectAttempts:0,lastMessage:undefined,connect:()=>{clearTimeout(this.realtime.timeout);this.realtime.timeout=window===null||window===void 0?void 0:window.setTimeout(()=>{this.realtime.createSocket();},50);},getTimeout:()=>{switch(true){case this.realtime.reconnectAttempts<5:return 1000;case this.realtime.reconnectAttempts<15:return 5000;case this.realtime.reconnectAttempts<100:return 10000;default:return 60000;}},createSocket:()=>{var _a,_b;if(this.realtime.channels.size<1)
|
||||
return;const channels=new URLSearchParams();channels.set('project',this.config.project);this.realtime.channels.forEach(channel=>{channels.append('channels[]',channel);});const url=this.config.endpointRealtime+'/realtime?'+channels.toString();if(url!==this.realtime.url||!this.realtime.socket||((_a=this.realtime.socket)===null||_a===void 0?void 0:_a.readyState)>WebSocket.OPEN){if(this.realtime.socket&&((_b=this.realtime.socket)===null||_b===void 0?void 0:_b.readyState)<WebSocket.CLOSING){this.realtime.reconnect=false;this.realtime.socket.close();}
|
||||
this.realtime.url=url;this.realtime.socket=new WebSocket(url);this.realtime.socket.addEventListener('message',this.realtime.onMessage);this.realtime.socket.addEventListener('open',_event=>{this.realtime.reconnectAttempts=0;});this.realtime.socket.addEventListener('close',event=>{var _a,_b,_c;if(!this.realtime.reconnect||(((_b=(_a=this.realtime)===null||_a===void 0?void 0:_a.lastMessage)===null||_b===void 0?void 0:_b.type)==='error'&&((_c=this.realtime)===null||_c===void 0?void 0:_c.lastMessage.data).code===1008)){this.realtime.reconnect=true;return;}
|
||||
const timeout=this.realtime.getTimeout();console.error(`Realtime got disconnected. Reconnect will be attempted in ${timeout / 1000} seconds.`,event.reason);setTimeout(()=>{this.realtime.reconnectAttempts++;this.realtime.createSocket();},timeout);});}},onMessage:(event)=>{var _a,_b;try{const message=JSON.parse(event.data);this.realtime.lastMessage=message;switch(message.type){case'connected':const cookie=JSON.parse((_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'{}');const session=cookie===null||cookie===void 0?void 0:cookie[`a_session_${this.config.project}`];const messageData=message.data;if(session&&!messageData.user){(_b=this.realtime.socket)===null||_b===void 0?void 0:_b.send(JSON.stringify({type:'authentication',data:{session}}));}
|
||||
|
|
@ -23,7 +23,9 @@ const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{
|
|||
if(typeof password==='undefined'){throw new AppwriteException('Missing required parameter: "password"');}
|
||||
let path='/account/email';let payload={};if(typeof email!=='undefined'){payload['email']=email;}
|
||||
if(typeof password!=='undefined'){payload['password']=password;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),createJWT:()=>__awaiter(this,void 0,void 0,function*(){let path='/account/jwt';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getLogs:()=>__awaiter(this,void 0,void 0,function*(){let path='/account/logs';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(name)=>__awaiter(this,void 0,void 0,function*(){if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),createJWT:()=>__awaiter(this,void 0,void 0,function*(){let path='/account/jwt';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getLogs:(limit,offset)=>__awaiter(this,void 0,void 0,function*(){let path='/account/logs';let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(name)=>__awaiter(this,void 0,void 0,function*(){if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
let path='/account/name';let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),updatePassword:(password,oldPassword)=>__awaiter(this,void 0,void 0,function*(){if(typeof password==='undefined'){throw new AppwriteException('Missing required parameter: "password"');}
|
||||
let path='/account/password';let payload={};if(typeof password!=='undefined'){payload['password']=password;}
|
||||
|
|
@ -223,7 +225,11 @@ if(typeof read!=='undefined'){payload['read']=read;}
|
|||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),deleteDocument:(collectionId,documentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/database/collections/{collectionId}/documents/{documentId}'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listIndexes:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/documents/{documentId}'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listDocumentLogs:(collectionId,documentId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/database/collections/{collectionId}/documents/{documentId}/logs'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),listIndexes:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createIndex:(collectionId,indexId,type,attributes,orders)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
|
||||
if(typeof type==='undefined'){throw new AppwriteException('Missing required parameter: "type"');}
|
||||
|
|
@ -236,8 +242,10 @@ const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{
|
|||
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
|
||||
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
|
||||
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listCollectionLogs:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/logs'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(range)=>__awaiter(this,void 0,void 0,function*(){let path='/database/usage';let payload={};if(typeof range!=='undefined'){payload['range']=range;}
|
||||
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listCollectionLogs:(collectionId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/logs'.replace('{collectionId}',collectionId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(range)=>__awaiter(this,void 0,void 0,function*(){let path='/database/usage';let payload={};if(typeof range!=='undefined'){payload['range']=range;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCollectionUsage:(collectionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/{collectionId}/usage'.replace('{collectionId}',collectionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
|
||||
|
|
@ -534,8 +542,10 @@ let path='/users/{userId}'.replace('{userId}',userId);let payload={};const uri=n
|
|||
let path='/users/{userId}'.replace('{userId}',userId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),updateEmail:(userId,email)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
if(typeof email==='undefined'){throw new AppwriteException('Missing required parameter: "email"');}
|
||||
let path='/users/{userId}/email'.replace('{userId}',userId);let payload={};if(typeof email!=='undefined'){payload['email']=email;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),getLogs:(userId)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
let path='/users/{userId}/logs'.replace('{userId}',userId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(userId,name)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),getLogs:(userId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
let path='/users/{userId}/logs'.replace('{userId}',userId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(userId,name)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
let path='/users/{userId}/name'.replace('{userId}',userId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),updatePassword:(userId,password)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
|
|
|
|||
388
public/dist/scripts/app.js
vendored
388
public/dist/scripts/app.js
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-ltr.css
vendored
2
public/dist/styles/default-ltr.css
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-rtl.css
vendored
2
public/dist/styles/default-rtl.css
vendored
File diff suppressed because one or more lines are too long
2697
public/scripts/dependencies/alpine.js
Normal file
2697
public/scripts/dependencies/alpine.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -48,7 +48,7 @@
|
|||
};
|
||||
this.headers = {
|
||||
'x-sdk-version': 'appwrite:web:4.0.4',
|
||||
'X-Appwrite-Response-Format': '0.12.0',
|
||||
'X-Appwrite-Response-Format': '0.11.0',
|
||||
};
|
||||
this.realtime = {
|
||||
socket: undefined,
|
||||
|
|
@ -321,12 +321,20 @@
|
|||
* Get currently logged in user list of latest security activity logs. Each
|
||||
* log returns user IP address, location and date and time of log.
|
||||
*
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getLogs: () => __awaiter(this, void 0, void 0, function* () {
|
||||
getLogs: (limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
let path = '/account/logs';
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
|
@ -1306,6 +1314,8 @@
|
|||
/**
|
||||
* Create Boolean Attribute
|
||||
*
|
||||
* Create a boolean attribute.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1347,6 +1357,8 @@
|
|||
/**
|
||||
* Create Email Attribute
|
||||
*
|
||||
* Create an email attribute.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1436,6 +1448,9 @@
|
|||
/**
|
||||
* Create Float Attribute
|
||||
*
|
||||
* Create a float attribute. Optionally, minimum and maximum values can be
|
||||
* provided.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1485,6 +1500,9 @@
|
|||
/**
|
||||
* Create Integer Attribute
|
||||
*
|
||||
* Create an integer attribute. Optionally, minimum and maximum values can be
|
||||
* provided.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1534,6 +1552,8 @@
|
|||
/**
|
||||
* Create IP Address Attribute
|
||||
*
|
||||
* Create IP address attribute.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1575,6 +1595,8 @@
|
|||
/**
|
||||
* Create String Attribute
|
||||
*
|
||||
* Create a new string attribute.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1623,6 +1645,8 @@
|
|||
/**
|
||||
* Create URL Attribute
|
||||
*
|
||||
* Create a URL attribute.
|
||||
*
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} attributeId
|
||||
|
|
@ -1894,6 +1918,38 @@
|
|||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* List Document Logs
|
||||
*
|
||||
* Get the document activity logs list by its unique ID.
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} documentId
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
listDocumentLogs: (collectionId, documentId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof documentId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "documentId"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/documents/{documentId}/logs'.replace('{collectionId}', collectionId).replace('{documentId}', documentId);
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* List Indexes
|
||||
*
|
||||
|
|
@ -2009,15 +2065,23 @@
|
|||
* Get the collection activity logs list by its unique ID.
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
listCollectionLogs: (collectionId) => __awaiter(this, void 0, void 0, function* () {
|
||||
listCollectionLogs: (collectionId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/logs'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
|
@ -4681,15 +4745,23 @@
|
|||
* Get the user activity logs list by its unique ID.
|
||||
*
|
||||
* @param {string} userId
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getLogs: (userId) => __awaiter(this, void 0, void 0, function* () {
|
||||
getLogs: (userId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof userId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "userId"');
|
||||
}
|
||||
let path = '/users/{userId}/logs'.replace('{userId}', userId);
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
window.ls.filter
|
||||
.add("avatar", function($value, element) {
|
||||
.add("avatar", function ($value, element) {
|
||||
if (!$value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let size = element.dataset["size"] || 80;
|
||||
let name = $value.name || $value || "";
|
||||
|
||||
|
||||
name = (typeof name !== 'string') ? '--' : name;
|
||||
|
||||
return def =
|
||||
"/v1/avatars/initials?project=console"+
|
||||
"/v1/avatars/initials?project=console" +
|
||||
"&name=" +
|
||||
encodeURIComponent(name) +
|
||||
"&width=" +
|
||||
|
|
@ -18,26 +18,26 @@ window.ls.filter
|
|||
"&height=" +
|
||||
size;
|
||||
})
|
||||
.add("selectedCollection", function($value, router) {
|
||||
.add("selectedCollection", function ($value, router) {
|
||||
return $value === router.params.collectionId ? "selected" : "";
|
||||
})
|
||||
.add("selectedDocument", function($value, router) {
|
||||
.add("selectedDocument", function ($value, router) {
|
||||
return $value === router.params.documentId ? "selected" : "";
|
||||
})
|
||||
.add("localeString", function($value) {
|
||||
.add("localeString", function ($value) {
|
||||
$value = parseInt($value);
|
||||
return !Number.isNaN($value) ? $value.toLocaleString() : "";
|
||||
})
|
||||
.add("date", function($value, date) {
|
||||
.add("date", function ($value, date) {
|
||||
return date.format("Y-m-d", $value);
|
||||
})
|
||||
.add("dateTime", function($value, date) {
|
||||
.add("dateTime", function ($value, date) {
|
||||
return date.format("Y-m-d H:i", $value);
|
||||
})
|
||||
.add("dateText", function($value, date) {
|
||||
.add("dateText", function ($value, date) {
|
||||
return date.format("d M Y", $value);
|
||||
})
|
||||
.add("timeSince", function($value) {
|
||||
.add("timeSince", function ($value) {
|
||||
$value = $value * 1000;
|
||||
|
||||
let seconds = Math.floor((Date.now() - $value) / 1000);
|
||||
|
|
@ -50,7 +50,7 @@ window.ls.filter
|
|||
}
|
||||
|
||||
let value = seconds;
|
||||
|
||||
|
||||
if (seconds >= 31536000) {
|
||||
value = Math.floor(seconds / 31536000);
|
||||
unit = "year";
|
||||
|
|
@ -71,10 +71,10 @@ window.ls.filter
|
|||
if (value != 1) {
|
||||
unit = unit + "s";
|
||||
}
|
||||
|
||||
|
||||
return value + " " + unit + " " + direction;
|
||||
})
|
||||
.add("ms2hum", function($value) {
|
||||
.add("ms2hum", function ($value) {
|
||||
let temp = $value;
|
||||
const years = Math.floor(temp / 31536000),
|
||||
days = Math.floor((temp %= 31536000) / 86400),
|
||||
|
|
@ -95,37 +95,37 @@ window.ls.filter
|
|||
|
||||
return "< 1s";
|
||||
})
|
||||
.add("seconds2hum", function($value) {
|
||||
.add("seconds2hum", function ($value) {
|
||||
|
||||
var seconds = ($value).toFixed(3);
|
||||
var seconds = ($value).toFixed(3);
|
||||
|
||||
var minutes = ($value / (60)).toFixed(1);
|
||||
var minutes = ($value / (60)).toFixed(1);
|
||||
|
||||
var hours = ($value / (60 * 60)).toFixed(1);
|
||||
var hours = ($value / (60 * 60)).toFixed(1);
|
||||
|
||||
var days = ($value / (60 * 60 * 24)).toFixed(1);
|
||||
var days = ($value / (60 * 60 * 24)).toFixed(1);
|
||||
|
||||
if (seconds < 60) {
|
||||
return seconds + "s";
|
||||
} else if (minutes < 60) {
|
||||
return minutes + "m";
|
||||
} else if (hours < 24) {
|
||||
return hours + "h";
|
||||
} else {
|
||||
return days + "d"
|
||||
}
|
||||
if (seconds < 60) {
|
||||
return seconds + "s";
|
||||
} else if (minutes < 60) {
|
||||
return minutes + "m";
|
||||
} else if (hours < 24) {
|
||||
return hours + "h";
|
||||
} else {
|
||||
return days + "d"
|
||||
}
|
||||
})
|
||||
.add("markdown", function($value, markdown) {
|
||||
.add("markdown", function ($value, markdown) {
|
||||
return markdown.render($value);
|
||||
})
|
||||
.add("pageCurrent", function($value, env) {
|
||||
.add("pageCurrent", function ($value, env) {
|
||||
return Math.ceil(parseInt($value || 0) / env.PAGING_LIMIT) + 1;
|
||||
})
|
||||
.add("pageTotal", function($value, env) {
|
||||
.add("pageTotal", function ($value, env) {
|
||||
let total = Math.ceil(parseInt($value || 0) / env.PAGING_LIMIT);
|
||||
return total ? total : 1;
|
||||
})
|
||||
.add("humanFileSize", function($value) {
|
||||
.add("humanFileSize", function ($value) {
|
||||
if (!$value) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ window.ls.filter
|
|||
|
||||
return $value.toFixed(1);
|
||||
})
|
||||
.add("humanFileUnit", function($value) {
|
||||
.add("humanFileUnit", function ($value) {
|
||||
if (!$value) {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -167,104 +167,111 @@ window.ls.filter
|
|||
|
||||
return units[u];
|
||||
})
|
||||
.add("statsTotal", function($value) {
|
||||
.add("statsTotal", function ($value) {
|
||||
if (!$value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$value = abbreviate($value, 0, false, false);
|
||||
|
||||
return $value === "0" ? "N/A" : $value;
|
||||
return $value ?? "N/A";
|
||||
})
|
||||
.add("isEmpty", function($value) {
|
||||
.add("statsGetLast", function ($value) {
|
||||
if (!$value || $value.length < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $value[$value.length - 1].value;
|
||||
})
|
||||
.add("isEmpty", function ($value) {
|
||||
return (!!$value);
|
||||
})
|
||||
.add("isEmptyObject", function($value) {
|
||||
.add("isEmptyObject", function ($value) {
|
||||
return ((Object.keys($value).length === 0 && $value.constructor === Object) || $value.length === 0)
|
||||
})
|
||||
.add("activeDomainsCount", function($value) {
|
||||
.add("activeDomainsCount", function ($value) {
|
||||
let result = [];
|
||||
|
||||
if(Array.isArray($value)) {
|
||||
result = $value.filter(function(node) {
|
||||
|
||||
if (Array.isArray($value)) {
|
||||
result = $value.filter(function (node) {
|
||||
return (node.verification && node.certificateId);
|
||||
});
|
||||
}
|
||||
|
||||
return result.length;
|
||||
})
|
||||
.add("documentAction", function(container) {
|
||||
.add("documentAction", function (container) {
|
||||
let collection = container.get('project-collection');
|
||||
let document = container.get('project-document');
|
||||
|
||||
if(collection && document && !document.$id) {
|
||||
if (collection && document && !document.$id) {
|
||||
return 'database.createDocument';
|
||||
}
|
||||
|
||||
return 'database.updateDocument';
|
||||
})
|
||||
.add("documentSuccess", function(container) {
|
||||
.add("documentSuccess", function (container) {
|
||||
let document = container.get('project-document');
|
||||
|
||||
if(document && !document.$id) {
|
||||
if (document && !document.$id) {
|
||||
return ',redirect';
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("firstElement", function($value) {
|
||||
if($value && $value[0]) {
|
||||
.add("firstElement", function ($value) {
|
||||
if ($value && $value[0]) {
|
||||
return $value[0];
|
||||
}
|
||||
|
||||
return $value;
|
||||
})
|
||||
.add("platformsLimit", function($value) {
|
||||
.add("platformsLimit", function ($value) {
|
||||
return $value;
|
||||
})
|
||||
.add("limit", function($value) {
|
||||
.add("limit", function ($value) {
|
||||
let postfix = ($value.length >= 50) ? '...' : '';
|
||||
return $value.substring(0, 50) + postfix;
|
||||
;
|
||||
})
|
||||
.add("arraySentence", function($value) {
|
||||
if(!Array.isArray($value)) {
|
||||
.add("arraySentence", function ($value) {
|
||||
if (!Array.isArray($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value.join(", ").replace(/,\s([^,]+)$/, ' and $1');
|
||||
})
|
||||
.add("runtimeName", function($value, env) {
|
||||
if(env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
.add("runtimeName", function ($value, env) {
|
||||
if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
return env.RUNTIMES[$value].name;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("runtimeLogo", function($value, env) {
|
||||
if(env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
.add("runtimeLogo", function ($value, env) {
|
||||
if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
return env.RUNTIMES[$value].logo;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("runtimeVersion", function($value, env) {
|
||||
if(env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
.add("runtimeVersion", function ($value, env) {
|
||||
if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
return env.RUNTIMES[$value].version;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("indexAttributes", function($value) {
|
||||
.add("indexAttributes", function ($value) {
|
||||
let output = '';
|
||||
|
||||
for(let i = 0; i < $value.attributes.length; i++) {
|
||||
for (let i = 0; i < $value.attributes.length; i++) {
|
||||
output += $value.attributes[i] + ' (' + $value.orders[i] + '), '
|
||||
}
|
||||
return output.slice(0, -2);
|
||||
})
|
||||
.add("collectionAttributes", function($value) {
|
||||
if(!Array.isArray($value)) {
|
||||
.add("collectionAttributes", function ($value) {
|
||||
if (!Array.isArray($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -274,17 +281,23 @@ window.ls.filter
|
|||
|
||||
return $value;
|
||||
})
|
||||
.add("documentAttribute", function($value, attribute) {
|
||||
if($value[attribute.key]) {
|
||||
.add("documentAttribute", function ($value, attribute) {
|
||||
if (attribute.key in $value) {
|
||||
return $value[attribute.key];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
})
|
||||
.add("accessProject", function($value, router) {
|
||||
.add("accessProject", function ($value, router) {
|
||||
return ($value && $value.hasOwnProperty(router.params.project)) ? $value[router.params.project] : 0;
|
||||
})
|
||||
;
|
||||
.add("first", function ($value) {
|
||||
return $value[0].$id;
|
||||
})
|
||||
.add("last", function ($value) {
|
||||
return $value[$value.length - 1].$id;
|
||||
})
|
||||
;
|
||||
|
||||
function abbreviate(number, maxPlaces, forcePlaces, forceLetter) {
|
||||
number = Number(number);
|
||||
|
|
|
|||
|
|
@ -123,3 +123,66 @@ window.addEventListener("load", async () => {
|
|||
}
|
||||
});
|
||||
|
||||
window.formValidation = (form, fields) => {
|
||||
const elements = form.elements;
|
||||
const actionHandler = (action, attribute) => {
|
||||
switch (action) {
|
||||
case "disable":
|
||||
elements[attribute].setAttribute("disabled", true);
|
||||
elements[attribute].dispatchEvent(new Event('change'));
|
||||
break;
|
||||
case "enable":
|
||||
elements[attribute].removeAttribute("disabled");
|
||||
elements[attribute].dispatchEvent(new Event('change'));
|
||||
break;
|
||||
case "unvalue":
|
||||
elements[attribute].value = "";
|
||||
break;
|
||||
case "check":
|
||||
elements[attribute].value = "true";
|
||||
break;
|
||||
case "uncheck":
|
||||
elements[attribute].value = "false";
|
||||
break;
|
||||
}
|
||||
};
|
||||
for (const field in fields) {
|
||||
for (const attribute in fields[field]) {
|
||||
const attr = fields[field][attribute];
|
||||
if (Array.isArray(attr)) {
|
||||
attr.forEach(action => {
|
||||
if (elements[field].value === "true") {
|
||||
actionHandler(action, attribute);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const condition = attr.if.some(c => {
|
||||
return elements[c].value === "true";
|
||||
});
|
||||
if (condition) {
|
||||
for (const thenAction in attr.then) {
|
||||
attr.then[thenAction].forEach(action => {
|
||||
actionHandler(action, thenAction);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const elseAction in attr.else) {
|
||||
attr.else[elseAction].forEach(action => {
|
||||
actionHandler(action, elseAction);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
form.addEventListener("reset", () => {
|
||||
for (const key in fields) {
|
||||
if (Object.hasOwnProperty.call(fields, key)) {
|
||||
const element = form.elements[key];
|
||||
element.setAttribute("value", "");
|
||||
element.removeAttribute("disabled");
|
||||
element.dispatchEvent(new Event("change"));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,6 +113,13 @@ window.ls.router
|
|||
scope: "console",
|
||||
project: true
|
||||
})
|
||||
.add("/console/database/usage", {
|
||||
template: function(window) {
|
||||
return window.location.pathname + window.location.search + '&version=' + APP_ENV.CACHEBUSTER;
|
||||
},
|
||||
scope: "console",
|
||||
project: true
|
||||
})
|
||||
.add("/console/database/collection", {
|
||||
template: function(window) {
|
||||
return window.location.pathname + window.location.search + '&version=' + APP_ENV.CACHEBUSTER;
|
||||
|
|
@ -146,6 +153,13 @@ window.ls.router
|
|||
scope: "console",
|
||||
project: true
|
||||
})
|
||||
.add("/console/storage/usage", {
|
||||
template: function(window) {
|
||||
return window.location.pathname + window.location.search + '&version=' + APP_ENV.CACHEBUSTER;
|
||||
},
|
||||
scope: "console",
|
||||
project: true
|
||||
})
|
||||
.add("/console/storage/:tab", {
|
||||
template: "/console/storage?version=" + APP_ENV.CACHEBUSTER,
|
||||
scope: "console",
|
||||
|
|
|
|||
|
|
@ -13,11 +13,20 @@
|
|||
case 'integer':
|
||||
value = parseInt(value);
|
||||
break;
|
||||
case 'float':
|
||||
value = parseFloat(parseFloat(value).toFixed(2));
|
||||
break;
|
||||
case 'numeric':
|
||||
value = Number(value);
|
||||
break;
|
||||
case 'float':
|
||||
value = parseFloat(value);
|
||||
break;
|
||||
case 'string':
|
||||
value = value.toString();
|
||||
if (value.length === 0) {
|
||||
value = null;
|
||||
}
|
||||
break;
|
||||
case 'json':
|
||||
value = (value) ? JSON.parse(value) : [];
|
||||
|
|
@ -105,7 +114,7 @@
|
|||
json[name] = element.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
json[name] = cast(json[name], castTo); // Apply casting
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
(function(window) {
|
||||
(function (window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-chart",
|
||||
controller: function(element, container, date, document) {
|
||||
controller: function (element, container, date, document) {
|
||||
let wrapper = document.createElement("div");
|
||||
let child = document.createElement("canvas");
|
||||
let sources = element.getAttribute('data-forms-chart');
|
||||
let width = element.getAttribute('data-width') || 500;
|
||||
let height = element.getAttribute('data-height') || 175;
|
||||
let showXAxis = element.getAttribute('data-show-x-axis') || false;
|
||||
let showYAxis = element.getAttribute('data-show-y-axis') || false;
|
||||
let colors = (element.getAttribute('data-colors') || 'blue,green,orange,red').split(',');
|
||||
let themes = {'blue': '#29b5d9', 'green': '#4eb55b', 'orange': '#fba233', 'red': '#dc3232',};
|
||||
let range = {'24h': 'H:i', '7d': 'd F Y', '30d': 'd F Y', '90d': 'd F Y'}
|
||||
let themes = { 'blue': '#29b5d9', 'green': '#4eb55b', 'orange': '#fba233', 'red': '#dc3232', 'create': '#00b680', 'read': '#009cde', 'update': '#696fd7', 'delete': '#da5d95', };
|
||||
let range = { '24h': 'H:i', '7d': 'd F Y', '30d': 'd F Y', '90d': 'd F Y' }
|
||||
|
||||
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
||||
|
||||
wrapper.classList.add('content');
|
||||
|
||||
|
||||
child.width = width;
|
||||
child.height = height;
|
||||
|
||||
|
|
@ -26,7 +28,7 @@
|
|||
|
||||
let chart = null;
|
||||
|
||||
let check = function() {
|
||||
let check = function () {
|
||||
|
||||
let config = {
|
||||
type: "line",
|
||||
|
|
@ -36,21 +38,19 @@
|
|||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
tooltip: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
caretPadding: 0
|
||||
},
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
intersect: true
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: false
|
||||
display: showXAxis
|
||||
},
|
||||
y: {
|
||||
display: false
|
||||
display: showYAxis,
|
||||
ticks: {
|
||||
fontColor: "#8f8f8f"
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
|
|
@ -61,6 +61,11 @@
|
|||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
caretPadding: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -68,7 +73,8 @@
|
|||
for (let i = 0; i < sources.length; i++) {
|
||||
let label = sources[i].substring(0, sources[i].indexOf('='));
|
||||
let path = sources[i].substring(sources[i].indexOf('=') + 1);
|
||||
let data = container.path(path);
|
||||
let usage = container.get('usage');
|
||||
let data = usage[path];
|
||||
let value = JSON.parse(element.value);
|
||||
|
||||
config.data.labels[i] = label;
|
||||
|
|
@ -80,27 +86,25 @@
|
|||
config.data.datasets[i].data = [0, 0, 0, 0, 0, 0, 0];
|
||||
config.data.datasets[i].fill = true;
|
||||
|
||||
if(!data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let dateFormat = (value.range && range[value.range]) ? range[value.range] : 'd F Y';
|
||||
|
||||
|
||||
for (let x = 0; x < data.length; x++) {
|
||||
config.data.datasets[i].data[x] = data[x].value;
|
||||
config.data.labels[x] = date.format(dateFormat, data[x].date);
|
||||
}
|
||||
}
|
||||
|
||||
if(chart) {
|
||||
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
else {
|
||||
}
|
||||
chart = new Chart(child.getContext("2d"), config);
|
||||
|
||||
wrapper.dataset["canvas"] = true;
|
||||
|
||||
}
|
||||
|
||||
check();
|
||||
|
|
@ -108,4 +112,4 @@
|
|||
element.addEventListener('change', check);
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
})(window);
|
||||
|
|
@ -3,13 +3,16 @@
|
|||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-clone",
|
||||
controller: function(element, document, view) {
|
||||
controller: function(element, document, view, expression) {
|
||||
element.removeAttribute('data-forms-clone');
|
||||
view.render(element);
|
||||
var template = element.innerHTML.toString();
|
||||
var label = element.dataset["label"] || "Add";
|
||||
var icon = element.dataset["icon"] || null;
|
||||
var target = element.dataset["target"] || null;
|
||||
var target = expression.parse(element.dataset["target"] || null);
|
||||
var first = parseInt(element.dataset["first"] || 1);
|
||||
var button = document.createElement("button");
|
||||
var debug = element.dataset["debug"] || false;
|
||||
|
||||
button.type = "button";
|
||||
button.innerText = " " + label + " ";
|
||||
|
|
@ -42,6 +45,11 @@
|
|||
|
||||
view.render(clone);
|
||||
|
||||
if(debug) {
|
||||
console.log('Debug: clone: ', clone);
|
||||
console.log('Debug: target: ', target);
|
||||
}
|
||||
|
||||
if (target) {
|
||||
target.appendChild(clone);
|
||||
} else {
|
||||
|
|
|
|||
16
public/scripts/views/forms/ip-address.js
Normal file
16
public/scripts/views/forms/ip-address.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-ip-address",
|
||||
controller: function(element) {
|
||||
element.setAttribute("minlength", "7");
|
||||
element.setAttribute("maxlength", "15");
|
||||
element.setAttribute("size", "15");
|
||||
element.setAttribute("autocomplete", "off");
|
||||
element.setAttribute("title", "Please provide IPv4 or IPv6 address");
|
||||
|
||||
element.setAttribute("pattern", "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))");
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
15
public/scripts/views/forms/required.js
Normal file
15
public/scripts/views/forms/required.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-required",
|
||||
controller: function(element, expression) {
|
||||
const isRequired = expression.parse(element.getAttribute('data-forms-required')) === "true";
|
||||
if (isRequired) {
|
||||
element.setAttribute("required", true);
|
||||
} else {
|
||||
element.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
16
public/scripts/views/forms/selected.js
Normal file
16
public/scripts/views/forms/selected.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-selected",
|
||||
controller: function(element, expression) {
|
||||
const isSelected = expression.parse(element.getAttribute('data-forms-selected')) === element.getAttribute('value');
|
||||
|
||||
if (isSelected) {
|
||||
element.setAttribute("selected", true);
|
||||
} else {
|
||||
element.removeAttribute("selected");
|
||||
}
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
|
|
@ -21,8 +21,15 @@
|
|||
|
||||
let syncB = function() {
|
||||
input.checked = (element.value === "true");
|
||||
|
||||
if (element.disabled) {
|
||||
input.setAttribute("disabled", true);
|
||||
} else {
|
||||
input.removeAttribute("disabled");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
input.addEventListener("input", syncA);
|
||||
input.addEventListener("change", syncA);
|
||||
|
||||
|
|
|
|||
|
|
@ -154,12 +154,12 @@
|
|||
case 'document':
|
||||
document[element.key] = element.default || {'$id': '', '$collection': '', '$permissions': {}};
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
document[element.key] = null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(element.array) {
|
||||
document[element.key] = [];
|
||||
}
|
||||
|
|
@ -395,7 +395,9 @@
|
|||
"failureParam" +
|
||||
parsedFailure[i].charAt(0).toUpperCase() +
|
||||
parsedFailure[i].slice(1),
|
||||
{}
|
||||
{
|
||||
text: exception.message ?? undefined
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,9 @@
|
|||
.icon-ok-circled:before { content: '\e81a'; } /* '' */
|
||||
.icon-warning:before { content: '\e81b'; } /* '' */
|
||||
.icon-mail:before { content: '\e81c'; } /* '' */
|
||||
.icon-email:before { content: '\e81c'; } /* '' */
|
||||
.icon-link:before { content: '\e81d'; } /* '' */
|
||||
.icon-url:before { content: '\e81d'; } /* '' */
|
||||
.icon-key-inv:before { content: '\e81e'; } /* '' */
|
||||
.icon-trash:before { content: '\e81f'; } /* '' */
|
||||
.icon-download:before { content: '\e820'; } /* '' */
|
||||
|
|
@ -134,6 +136,8 @@
|
|||
.icon-string:before { content: '\e852'; } /* '' */
|
||||
.icon-integer:before { content: '\e853'; } /* '' */
|
||||
.icon-float:before { content: '\e854'; } /* '' */
|
||||
.icon-double:before { content: '\e854'; } /* '' */
|
||||
.icon-enum:before { content: '\e812'; } /* '' */
|
||||
.icon-ip:before { content: '\e855'; } /* '' */
|
||||
.icon-more:before { content: '\e856'; } /* '' */
|
||||
.icon-key:before { content: '\e857'; } /* '' */
|
||||
|
|
|
|||
|
|
@ -589,7 +589,13 @@
|
|||
background-repeat: round;
|
||||
border: solid 1px var(--config-border-color);
|
||||
border-right: solid 1px transparent;
|
||||
border-bottom: solid 1px transparent;
|
||||
border-bottom: solid 1px transparent;
|
||||
&.background-image-no {
|
||||
background-image: none;
|
||||
}
|
||||
&.border-no {
|
||||
border: none;
|
||||
}
|
||||
@media @tablets, @phones {
|
||||
width: 100%;
|
||||
padding-bottom: 32%;
|
||||
|
|
@ -608,6 +614,41 @@
|
|||
.chart-notes {
|
||||
font-size: 12px;
|
||||
|
||||
&.crud li {
|
||||
&:nth-child(1), &.create {
|
||||
color: #00b680;
|
||||
|
||||
&::before {
|
||||
background: #00b680;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2), &.read {
|
||||
color: #009cde;
|
||||
|
||||
&::before {
|
||||
background: #009cde;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(3), &.update {
|
||||
color: #696fd7;
|
||||
|
||||
&::before {
|
||||
background: #696fd7;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(4), &.delete {
|
||||
color: #da5d95;
|
||||
|
||||
&::before {
|
||||
background: #da5d95;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 20px;
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -16,35 +16,35 @@ class UsageBuckets extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('files.count', [
|
||||
->addRule('filesCount', [
|
||||
'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', [
|
||||
->addRule('filesCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for files created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('files.read', [
|
||||
->addRule('filesRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for files read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('files.update', [
|
||||
->addRule('filesUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for files updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('files.delete', [
|
||||
->addRule('filesDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for files deleted.',
|
||||
'default' => [],
|
||||
|
|
|
|||
|
|
@ -16,35 +16,35 @@ class UsageCollection extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('documents.count', [
|
||||
->addRule('documentsCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.create', [
|
||||
->addRule('documentsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.read', [
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.update', [
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.delete', [
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
|
|
|
|||
|
|
@ -16,70 +16,70 @@ class UsageDatabase extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('documents.count', [
|
||||
->addRule('documentsCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.count', [
|
||||
->addRule('collectionsCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of collections.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.create', [
|
||||
->addRule('documentsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.read', [
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.update', [
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.delete', [
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.create', [
|
||||
->addRule('collectionsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.read', [
|
||||
->addRule('collectionsRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.update', [
|
||||
->addRule('collectionsUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.delete', [
|
||||
->addRule('collectionsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections delete.',
|
||||
'default' => [],
|
||||
|
|
|
|||
|
|
@ -16,21 +16,21 @@ class UsageFunctions extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('functions.executions', [
|
||||
->addRule('functionsExecutions', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for function executions.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('functions.failures', [
|
||||
->addRule('functionsFailures', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for function execution failures.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('functions.compute', [
|
||||
->addRule('functionsCompute', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for function execution duration.',
|
||||
'default' => [],
|
||||
|
|
|
|||
|
|
@ -16,56 +16,56 @@ class UsageUsers extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('users.count', [
|
||||
->addRule('usersCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of users.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.create', [
|
||||
->addRule('usersCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.read', [
|
||||
->addRule('usersRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.update', [
|
||||
->addRule('usersUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.delete', [
|
||||
->addRule('usersDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users deleted.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessions.create', [
|
||||
->addRule('sessionsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for sessions created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessions.provider.create', [
|
||||
->addRule('sessionsProviderCreate', [
|
||||
'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', [
|
||||
->addRule('sessionsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for sessions deleted.',
|
||||
'default' => [],
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideConsole;
|
||||
|
||||
public function testCreateCollection():array
|
||||
public function testCreateCollection(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -63,16 +63,16 @@ class DatabaseConsoleClientTest extends Scope
|
|||
$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']);
|
||||
$this->assertIsArray($response['body']['documentsCount']);
|
||||
$this->assertIsArray($response['body']['collectionsCount']);
|
||||
$this->assertIsArray($response['body']['documentsCreate']);
|
||||
$this->assertIsArray($response['body']['documentsRead']);
|
||||
$this->assertIsArray($response['body']['documentsUpdate']);
|
||||
$this->assertIsArray($response['body']['documentsDelete']);
|
||||
$this->assertIsArray($response['body']['collectionsCreate']);
|
||||
$this->assertIsArray($response['body']['collectionsRead']);
|
||||
$this->assertIsArray($response['body']['collectionsUpdate']);
|
||||
$this->assertIsArray($response['body']['collectionsDelete']);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([
|
||||
$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()), [
|
||||
|
|
@ -106,7 +106,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([
|
||||
$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()), [
|
||||
|
|
@ -116,11 +116,11 @@ class DatabaseConsoleClientTest extends Scope
|
|||
$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']);
|
||||
$this->assertIsArray($response['body']['documentsCount']);
|
||||
$this->assertIsArray($response['body']['documentsCreate']);
|
||||
$this->assertIsArray($response['body']['documentsRead']);
|
||||
$this->assertIsArray($response['body']['documentsUpdate']);
|
||||
$this->assertIsArray($response['body']['documentsDelete']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,4 +176,4 @@ class DatabaseConsoleClientTest extends Scope
|
|||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideConsole;
|
||||
|
||||
public function testCreateFunction():array
|
||||
public function testCreateFunction(): array
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
|
@ -20,7 +20,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'name' => 'Test',
|
||||
'execute' => ['user:'.$this->getUser()['$id']],
|
||||
'execute' => ['user:' . $this->getUser()['$id']],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
|
@ -41,7 +41,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
'functionId' => $function['body']['$id']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateFunction
|
||||
*/
|
||||
|
|
@ -51,7 +51,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([
|
||||
$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()), [
|
||||
|
|
@ -73,7 +73,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([
|
||||
$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()), [
|
||||
|
|
@ -83,9 +83,8 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$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']);
|
||||
$this->assertIsArray($response['body']['functionsExecutions']);
|
||||
$this->assertIsArray($response['body']['functionsFailures']);
|
||||
$this->assertIsArray($response['body']['functionsCompute']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class HealthCustomServerTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testHTTPSuccess():array
|
||||
public function testHTTPSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -30,11 +30,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testDBSuccess():array
|
||||
public function testDBSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -50,11 +50,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testCacheSuccess():array
|
||||
public function testCacheSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -70,11 +70,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testTimeSuccess():array
|
||||
public function testTimeSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -94,11 +94,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testWebhooksSuccess():array
|
||||
public function testWebhooksSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -115,11 +115,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testLogsSuccess():array
|
||||
public function testLogsSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -136,11 +136,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testUsageSuccess():array
|
||||
public function testUsageSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -157,11 +157,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testCertificatesSuccess():array
|
||||
public function testCertificatesSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -178,11 +178,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testStorageLocalSuccess():array
|
||||
public function testStorageLocalSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -198,11 +198,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testStorageAntiVirusSuccess():array
|
||||
public function testStorageAntiVirusSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
|
@ -220,7 +220,7 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ trait RealtimeBase
|
|||
$this->expectException(ConnectionException::class); // Check if server disconnnected client
|
||||
$client->close();
|
||||
|
||||
$client = new WebSocketClient('ws://appwrite-traefik/v1/realtime', [
|
||||
$client = new WebSocketClient('ws://appwrite-traefik/v1/realtime?channels[]=files"', [
|
||||
'headers' => [
|
||||
'Origin' => 'appwrite.test'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class StorageConsoleClientTest extends Scope
|
|||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
|
@ -83,10 +83,10 @@ class StorageConsoleClientTest extends Scope
|
|||
$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']);
|
||||
$this->assertIsArray($response['body']['filesCount']);
|
||||
$this->assertIsArray($response['body']['filesCreate']);
|
||||
$this->assertIsArray($response['body']['filesRead']);
|
||||
$this->assertIsArray($response['body']['filesUpdate']);
|
||||
$this->assertIsArray($response['body']['filesDelete']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class UsersConsoleClientTest extends Scope
|
|||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
|
@ -52,15 +52,15 @@ class UsersConsoleClientTest extends Scope
|
|||
$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']);
|
||||
|
||||
$this->assertIsArray($response['body']['usersCount']);
|
||||
$this->assertIsArray($response['body']['usersCreate']);
|
||||
$this->assertIsArray($response['body']['usersRead']);
|
||||
$this->assertIsArray($response['body']['usersUpdate']);
|
||||
$this->assertIsArray($response['body']['usersDelete']);
|
||||
$this->assertIsArray($response['body']['sessionsCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsProviderCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsDelete']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
|
|
@ -71,13 +71,13 @@ class UsersConsoleClientTest extends Scope
|
|||
$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']);
|
||||
$this->assertIsArray($response['body']['usersCount']);
|
||||
$this->assertIsArray($response['body']['usersCreate']);
|
||||
$this->assertIsArray($response['body']['usersRead']);
|
||||
$this->assertIsArray($response['body']['usersUpdate']);
|
||||
$this->assertIsArray($response['body']['usersDelete']);
|
||||
$this->assertIsArray($response['body']['sessionsCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsProviderCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsDelete']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue