1
0
Fork 0
mirror of synced 2024-09-20 03:17:30 +12:00

Merge branch 'main' into mock-numbers

This commit is contained in:
Christy Jacob 2024-02-20 12:27:14 +05:30 committed by GitHub
commit 35562715ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 160 additions and 106 deletions

2
.env
View file

@ -78,7 +78,7 @@ _APP_MAINTENANCE_RETENTION_CACHE=2592000
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600 _APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400 _APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_AUDIT=1209600 _APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_USAGE_AGGREGATION_INTERVAL=20 _APP_USAGE_AGGREGATION_INTERVAL=30
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000 _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400 _APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_USAGE_STATS=enabled _APP_USAGE_STATS=enabled

View file

@ -34,6 +34,28 @@ $commonCollections = [
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
[
'$id' => 'resourceType',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('mimeType'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[ [
'$id' => 'accessedAt', '$id' => 'accessedAt',
'type' => Database::VAR_DATETIME, 'type' => Database::VAR_DATETIME,
@ -1327,10 +1349,10 @@ $commonCollections = [
] ]
], ],
'stats_v2' => [ 'stats' => [
'$collection' => ID::custom(Database::METADATA), '$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('stats_v2'), '$id' => ID::custom('stats'),
'name' => 'stats_v2', 'name' => 'stats',
'attributes' => [ 'attributes' => [
[ [
'$id' => ID::custom('metric'), '$id' => ID::custom('metric'),

View file

@ -76,7 +76,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
} }
if (empty($gitHubSession)) { if (empty($gitHubSession)) {
throw new Exception(Exception::GENERAL_UNKNOWN, 'GitHub session not found.'); throw new Exception(Exception::USER_SESSION_NOT_FOUND, 'GitHub session not found.');
} }
$provider = $gitHubSession->getAttribute('provider', ''); $provider = $gitHubSession->getAttribute('provider', '');

View file

@ -3563,7 +3563,7 @@ App::get('/v1/databases/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -3571,7 +3571,7 @@ App::get('/v1/databases/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),
@ -3647,7 +3647,7 @@ App::get('/v1/databases/:databaseId/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -3655,7 +3655,7 @@ App::get('/v1/databases/:databaseId/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),
@ -3733,7 +3733,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -3741,7 +3741,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),

View file

@ -484,7 +484,7 @@ App::get('/v1/functions/:functionId/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -492,7 +492,7 @@ App::get('/v1/functions/:functionId/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),
@ -576,7 +576,7 @@ App::get('/v1/functions/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -584,7 +584,7 @@ App::get('/v1/functions/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),

View file

@ -73,7 +73,7 @@ App::get('/v1/project/usage')
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) { Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) {
foreach ($metrics['total'] as $metric) { foreach ($metrics['total'] as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -81,7 +81,7 @@ App::get('/v1/project/usage')
} }
foreach ($metrics['period'] as $metric) { foreach ($metrics['period'] as $metric) {
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::greaterThanEqual('time', $firstDay), Query::greaterThanEqual('time', $firstDay),
@ -116,7 +116,7 @@ App::get('/v1/project/usage')
$id = $function->getId(); $id = $function->getId();
$name = $function->getAttribute('name'); $name = $function->getAttribute('name');
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS); $metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS);
$value = $dbForProject->findOne('stats_v2', [ $value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -132,7 +132,7 @@ App::get('/v1/project/usage')
$id = $bucket->getId(); $id = $bucket->getId();
$name = $bucket->getAttribute('name'); $name = $bucket->getAttribute('name');
$metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE); $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE);
$value = $dbForProject->findOne('stats_v2', [ $value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -144,9 +144,30 @@ App::get('/v1/project/usage')
]; ];
}, $dbForProject->find('buckets')); }, $dbForProject->find('buckets'));
// merge network inbound + outbound
$projectBandwidth = [];
foreach ($usage[METRIC_NETWORK_INBOUND] as $item) {
$projectBandwidth[$item['date']] ??= 0;
$projectBandwidth[$item['date']] += $item['value'];
}
foreach ($usage[METRIC_NETWORK_OUTBOUND] as $item) {
$projectBandwidth[$item['date']] ??= 0;
$projectBandwidth[$item['date']] += $item['value'];
}
$network = [];
foreach ($projectBandwidth as $date => $value) {
$network[] = [
'date' => $date,
'value' => $value
];
}
$response->dynamic(new Document([ $response->dynamic(new Document([
'requests' => ($usage[METRIC_NETWORK_REQUESTS]), 'requests' => ($usage[METRIC_NETWORK_REQUESTS]),
'network' => ($usage[METRIC_NETWORK_INBOUND] + $usage[METRIC_NETWORK_OUTBOUND]), 'network' => $network,
'users' => ($usage[METRIC_USERS]), 'users' => ($usage[METRIC_USERS]),
'executions' => ($usage[METRIC_EXECUTIONS]), 'executions' => ($usage[METRIC_EXECUTIONS]),
'executionsTotal' => $total[METRIC_EXECUTIONS], 'executionsTotal' => $total[METRIC_EXECUTIONS],

View file

@ -1467,6 +1467,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
if ($deviceDeleted) { if ($deviceDeleted) {
$queueForDeletes $queueForDeletes
->setType(DELETE_TYPE_CACHE_BY_RESOURCE) ->setType(DELETE_TYPE_CACHE_BY_RESOURCE)
->setResourceType('bucket/' . $bucket->getId())
->setResource('file/' . $fileId) ->setResource('file/' . $fileId)
; ;
@ -1524,7 +1525,7 @@ App::get('/v1/storage/usage')
$total = []; $total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -1532,7 +1533,7 @@ App::get('/v1/storage/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),
@ -1609,7 +1610,7 @@ App::get('/v1/storage/:bucketId/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $metric) { foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -1617,7 +1618,7 @@ App::get('/v1/storage/:bucketId/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),

View file

@ -1266,7 +1266,7 @@ App::get('/v1/users/usage')
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $count => $metric) { foreach ($metrics as $count => $metric) {
$result = $dbForProject->findOne('stats_v2', [ $result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', ['inf']) Query::equal('period', ['inf'])
]); ]);
@ -1274,7 +1274,7 @@ App::get('/v1/users/usage')
$stats[$metric]['total'] = $result['value'] ?? 0; $stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit']; $limit = $days['limit'];
$period = $days['period']; $period = $days['period'];
$results = $dbForProject->find('stats_v2', [ $results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),

View file

@ -252,24 +252,22 @@ App::init()
; ;
$useCache = $route->getLabel('cache', false); $useCache = $route->getLabel('cache', false);
if ($useCache) { if ($useCache) {
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); $key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache( $cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
); );
$timestamp = 60 * 60 * 24 * 30; $timestamp = 60 * 60 * 24 * 30;
$data = $cache->load($key, $timestamp); $data = $cache->load($key, $timestamp);
if (!empty($data)) { if (!empty($data) && !$cacheLog->isEmpty()) {
$data = json_decode($data, true); $parts = explode('/', $cacheLog->getAttribute('resourceType'));
$parts = explode('/', $data['resourceType']);
$type = $parts[0] ?? null; $type = $parts[0] ?? null;
if ($type === 'bucket') { if ($type === 'bucket') {
$bucketId = $parts[1] ?? null; $bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@ -281,11 +279,12 @@ App::init()
$fileSecurity = $bucket->getAttribute('fileSecurity', false); $fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ); $validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead()); $valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) { if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED); throw new Exception(Exception::USER_UNAUTHORIZED);
} }
$parts = explode('/', $data['resource']); $parts = explode('/', $cacheLog->getAttribute('resource'));
$fileId = $parts[1] ?? null; $fileId = $parts[1] ?? null;
if ($fileSecurity && !$valid) { if ($fileSecurity && !$valid) {
@ -302,8 +301,8 @@ App::init()
$response $response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT') ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
->addHeader('X-Appwrite-Cache', 'hit') ->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($data['contentType']) ->setContentType($cacheLog->getAttribute('mimeType'))
->send(base64_decode($data['payload'])) ->send($data)
; ;
} else { } else {
$response->addHeader('X-Appwrite-Cache', 'miss'); $response->addHeader('X-Appwrite-Cache', 'miss');
@ -534,7 +533,6 @@ App::shutdown()
if ($useCache) { if ($useCache) {
$resource = $resourceType = null; $resource = $resourceType = null;
$data = $response->getPayload(); $data = $response->getPayload();
if (!empty($data['payload'])) { if (!empty($data['payload'])) {
$pattern = $route->getLabel('cache.resource', null); $pattern = $route->getLabel('cache.resource', null);
if (!empty($pattern)) { if (!empty($pattern)) {
@ -546,22 +544,17 @@ App::shutdown()
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user); $resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
} }
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); $key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
$data = json_encode([ $signature = md5($data['payload']);
'resourceType' => $resourceType, $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
'resource' => $resource,
'contentType' => $response->getContentType(),
'payload' => base64_encode($data['payload']),
]) ;
$signature = md5($data);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', ''); $accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now(); $now = DateTime::now();
if ($cacheLog->isEmpty()) { if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key, '$id' => $key,
'resource' => $resource, 'resource' => $resource,
'resourceType' => $resourceType,
'mimeType' => $response->getContentType(),
'accessedAt' => $now, 'accessedAt' => $now,
'signature' => $signature, 'signature' => $signature,
]))); ])));
@ -574,7 +567,7 @@ App::shutdown()
$cache = new Cache( $cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
); );
$cache->save($key, $data); $cache->save($key, $data['payload']);
} }
} }
} }

View file

@ -112,7 +112,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 329; const APP_CACHE_BUSTER = 330;
const APP_VERSION_STABLE = '1.4.13'; const APP_VERSION_STABLE = '1.4.13';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';

View file

@ -928,6 +928,7 @@ services:
# - appwrite # - appwrite
# volumes: # volumes:
# - appwrite-uploads:/storage/uploads # - appwrite-uploads:/storage/uploads
# Dev Tools Start ------------------------------------------------------------------------------------------ # Dev Tools Start ------------------------------------------------------------------------------------------
# #
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack # The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
@ -936,8 +937,9 @@ services:
# #
# MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI. # MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI.
# RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks # RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks
# RedisCommander - A nice UI for exploring Redis data # Redis Insight - A nice UI for exploring Redis data
# Webgrind - A nice UI for exploring and debugging code-level stuff # Adminer - A nice UI for exploring MariaDB data
# GraphQl Explorer - A nice UI for exploring GraphQL API
maildev: # used mainly for dev tests maildev: # used mainly for dev tests
image: appwrite/mailcatcher:1.0.0 image: appwrite/mailcatcher:1.0.0
@ -967,21 +969,15 @@ services:
networks: networks:
- appwrite - appwrite
# redis-commander: redis-insight:
# image: rediscommander/redis-commander:latest image: redis/redisinsight:latest
# restart: unless-stopped restart: unless-stopped
# networks: networks:
# - appwrite - appwrite
# environment: environment:
# - REDIS_HOSTS=redis - REDIS_HOSTS=redis
# ports: ports:
# - "8081:8081" - "8081:5540"
# webgrind:
# image: 'jokkedk/webgrind:latest'
# volumes:
# - './debug:/tmp'
# ports:
# - '3001:80'
graphql-explorer: graphql-explorer:
container_name: appwrite-graphql-explorer container_name: appwrite-graphql-explorer

View file

@ -10,6 +10,7 @@ class Delete extends Event
{ {
protected string $type = ''; protected string $type = '';
protected ?Document $document = null; protected ?Document $document = null;
protected ?string $resourceType = null;
protected ?string $resource = null; protected ?string $resource = null;
protected ?string $datetime = null; protected ?string $datetime = null;
protected ?string $hourlyUsageRetentionDatetime = null; protected ?string $hourlyUsageRetentionDatetime = null;
@ -107,6 +108,19 @@ class Delete extends Event
return $this; return $this;
} }
/**
* Sets the resource type for the delete event.
*
* @param string $resourceType
* @return self
*/
public function setResourceType(string $resourceType): self
{
$this->resourceType = $resourceType;
return $this;
}
/** /**
* Returns the set document for the delete event. * Returns the set document for the delete event.
* *
@ -133,6 +147,7 @@ class Delete extends Event
'type' => $this->type, 'type' => $this->type,
'document' => $this->document, 'document' => $this->document,
'resource' => $this->resource, 'resource' => $this->resource,
'resourceType' => $this->resourceType,
'datetime' => $this->datetime, 'datetime' => $this->datetime,
'hourlyUsageRetentionDatetime' => $this->hourlyUsageRetentionDatetime 'hourlyUsageRetentionDatetime' => $this->hourlyUsageRetentionDatetime
]); ]);

View file

@ -270,7 +270,7 @@ class CalcTierStats extends Action
$limit = $periods[$range]['limit']; $limit = $periods[$range]['limit'];
$period = $periods[$range]['period']; $period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats_v2', [ $requestDocs = $dbForProject->find('stats', [
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::limit($limit), Query::limit($limit),

View file

@ -167,8 +167,8 @@ class CreateInfMetric extends Action
try { try {
$id = \md5("_inf_{$metric}"); $id = \md5("_inf_{$metric}");
$dbForProject->deleteDocument('stats_v2', $id); $dbForProject->deleteDocument('stats', $id);
$dbForProject->createDocument('stats_v2', new Document([ $dbForProject->createDocument('stats', new Document([
'$id' => $id, '$id' => $id,
'metric' => $metric, 'metric' => $metric,
'period' => 'inf', 'period' => 'inf',
@ -190,7 +190,7 @@ class CreateInfMetric extends Action
protected function getFromMetric(database $dbForProject, string $metric): int|float protected function getFromMetric(database $dbForProject, string $metric): int|float
{ {
return $dbForProject->sum('stats_v2', 'value', [ return $dbForProject->sum('stats', 'value', [
Query::equal('metric', [ Query::equal('metric', [
$metric, $metric,
]), ]),

View file

@ -68,6 +68,7 @@ class Deletes extends Action
$datetime = $payload['datetime'] ?? null; $datetime = $payload['datetime'] ?? null;
$hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null; $hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null;
$resource = $payload['resource'] ?? null; $resource = $payload['resource'] ?? null;
$resourceType = $payload['resourceType'] ?? null;
$document = new Document($payload['document'] ?? []); $document = new Document($payload['document'] ?? []);
$project = new Document($payload['project'] ?? []); $project = new Document($payload['project'] ?? []);
@ -135,7 +136,7 @@ class Deletes extends Action
$this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime); $this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime);
break; break;
case DELETE_TYPE_CACHE_BY_RESOURCE: case DELETE_TYPE_CACHE_BY_RESOURCE:
$this->deleteCacheByResource($project, $getProjectDB, $resource); $this->deleteCacheByResource($project, $getProjectDB, $resource, $resourceType);
break; break;
case DELETE_TYPE_CACHE_BY_TIMESTAMP: case DELETE_TYPE_CACHE_BY_TIMESTAMP:
$this->deleteCacheByDate($project, $getProjectDB, $datetime); $this->deleteCacheByDate($project, $getProjectDB, $datetime);
@ -193,32 +194,37 @@ class Deletes extends Action
* @param string $resource * @param string $resource
* @return void * @return void
* @throws Authorization * @throws Authorization
* @param string|null $resourceType
* @throws Exception
*/ */
private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource, string $resourceType = null): void
{ {
$projectId = $project->getId(); $projectId = $project->getId();
$dbForProject = $getProjectDB($project); $dbForProject = $getProjectDB($project);
$document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]);
if ($document) { $cache = new Cache(
$cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) );
);
$this->deleteById( $query[] = Query::equal('resource', [$resource]);
$document, if (!empty($resourceType)) {
$dbForProject, $query[] = Query::equal('resourceType', [$resourceType]);
function ($document) use ($cache, $projectId) {
$path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
if ($cache->purge($document->getId())) {
Console::success('Deleting cache file: ' . $path);
} else {
Console::error('Failed to delete cache file: ' . $path);
}
}
);
} }
$this->deleteByGroup(
'cache',
$query,
$dbForProject,
function (Document $document) use ($cache, $projectId) {
$path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
if ($cache->purge($document->getId())) {
Console::success('Deleting cache file: ' . $path);
} else {
Console::error('Failed to delete cache file: ' . $path);
}
}
);
} }
/** /**
@ -269,7 +275,7 @@ class Deletes extends Action
{ {
$dbForProject = $getProjectDB($project); $dbForProject = $getProjectDB($project);
// Delete Usage stats // Delete Usage stats
$this->deleteByGroup('stats_v2', [ $this->deleteByGroup('stats', [
Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::lessThan('time', $hourlyUsageRetentionDatetime),
Query::equal('period', ['1h']), Query::equal('period', ['1h']),
], $dbForProject); ], $dbForProject);

View file

@ -286,7 +286,7 @@ class Hamster extends Action
$limit = $periodValue['limit']; $limit = $periodValue['limit'];
$period = $periodValue['period']; $period = $periodValue['period'];
$requestDocs = $dbForProject->find('stats_v2', [ $requestDocs = $dbForProject->find('stats', [
Query::equal('period', [$period]), Query::equal('period', [$period]),
Query::equal('metric', [$metric]), Query::equal('metric', [$metric]),
Query::limit($limit), Query::limit($limit),

View file

@ -129,8 +129,8 @@ class Usage extends Action
} }
break; break;
case $document->getCollection() === 'databases': // databases case $document->getCollection() === 'databases': // databases
$collections = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS)));
$documents = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS))); $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS)));
if (!empty($collections['value'])) { if (!empty($collections['value'])) {
$metrics[] = [ $metrics[] = [
'key' => METRIC_COLLECTIONS, 'key' => METRIC_COLLECTIONS,
@ -148,7 +148,7 @@ class Usage extends Action
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
$parts = explode('_', $document->getCollection()); $parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0; $databaseInternalId = $parts[1] ?? 0;
$documents = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS))); $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS)));
if (!empty($documents['value'])) { if (!empty($documents['value'])) {
$metrics[] = [ $metrics[] = [
@ -163,8 +163,8 @@ class Usage extends Action
break; break;
case $document->getCollection() === 'buckets': case $document->getCollection() === 'buckets':
$files = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
$storage = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
if (!empty($files['value'])) { if (!empty($files['value'])) {
$metrics[] = [ $metrics[] = [
@ -182,13 +182,13 @@ class Usage extends Action
break; break;
case $document->getCollection() === 'functions': case $document->getCollection() === 'functions':
$deployments = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS))); $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
$deploymentsStorage = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE))); $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
$builds = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS))); $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
$buildsStorage = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE))); $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
$buildsCompute = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE))); $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
$executions = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS))); $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
$executionsCompute = $dbForProject->getDocument('stats_v2', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE))); $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
if (!empty($deployments['value'])) { if (!empty($deployments['value'])) {
$metrics[] = [ $metrics[] = [

View file

@ -78,7 +78,7 @@ class UsageDump extends Action
$id = \md5("{$time}_{$period}_{$key}"); $id = \md5("{$time}_{$period}_{$key}");
try { try {
$dbForProject->createDocument('stats_v2', new Document([ $dbForProject->createDocument('stats', new Document([
'$id' => $id, '$id' => $id,
'period' => $period, 'period' => $period,
'time' => $time, 'time' => $time,
@ -89,14 +89,14 @@ class UsageDump extends Action
} catch (Duplicate $th) { } catch (Duplicate $th) {
if ($value < 0) { if ($value < 0) {
$dbForProject->decreaseDocumentAttribute( $dbForProject->decreaseDocumentAttribute(
'stats_v2', 'stats',
$id, $id,
'value', 'value',
abs($value) abs($value)
); );
} else { } else {
$dbForProject->increaseDocumentAttribute( $dbForProject->increaseDocumentAttribute(
'stats_v2', 'stats',
$id, $id,
'value', 'value',
$value $value