Merge pull request #10953 from appwrite/feat-audits-upgrade

Feat: Audits  upgrade
This commit is contained in:
Damodar Lohani
2025-12-31 06:55:34 +05:45
committed by GitHub
18 changed files with 212 additions and 134 deletions
+8 -6
View File
@@ -37,7 +37,7 @@ use libphonenumber\PhoneNumberUtil;
use MaxMind\Db\Reader;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Audit\Audit as EventAudit;
use Utopia\Audit\Audit;
use Utopia\Auth\Hashes\Sha;
use Utopia\Auth\Proofs\Code as ProofsCode;
use Utopia\Auth\Proofs\Password as ProofsPassword;
@@ -3033,7 +3033,8 @@ App::get('/v1/account/logs')
->inject('locale')
->inject('geodb')
->inject('dbForProject')
->action(function (array $queries, bool $includeTotal, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject) {
->inject('audit')
->action(function (array $queries, bool $includeTotal, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Audit $audit) {
try {
$queries = Query::parseQueries($queries);
@@ -3041,9 +3042,10 @@ App::get('/v1/account/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new EventAudit($dbForProject);
$logs = $audit->getLogsByUser($user->getSequence(), $queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$logs = $audit->getLogsByUser($user->getSequence(), offset: $offset, limit: $limit);
$output = [];
@@ -3072,7 +3074,7 @@ App::get('/v1/account/logs')
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence(), $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence()) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+32 -16
View File
@@ -1145,7 +1145,8 @@ App::get('/v1/messaging/providers/:providerId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $providerId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->inject('audit')
->action(function (string $providerId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
@@ -1158,9 +1159,12 @@ App::get('/v1/messaging/providers/:providerId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'provider/' . $providerId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, offset: $offset, limit: $limit);
$output = [];
foreach ($logs as $i => &$log) {
@@ -1207,7 +1211,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2549,7 +2553,8 @@ App::get('/v1/messaging/topics/:topicId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $topicId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->inject('audit')
->action(function (string $topicId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
$topic = $dbForProject->getDocument('topics', $topicId);
if ($topic->isEmpty()) {
@@ -2562,9 +2567,12 @@ App::get('/v1/messaging/topics/:topicId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'topic/' . $topicId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, offset: $offset, limit: $limit);
$output = [];
@@ -2612,7 +2620,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2966,7 +2974,8 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $subscriberId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->inject('audit')
->action(function (string $subscriberId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
$subscriber = $dbForProject->getDocument('subscribers', $subscriberId);
if ($subscriber->isEmpty()) {
@@ -2979,9 +2988,12 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'subscriber/' . $subscriberId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
$output = [];
@@ -3029,7 +3041,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -3762,7 +3774,8 @@ App::get('/v1/messaging/messages/:messageId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $messageId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->inject('audit')
->action(function (string $messageId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
$message = $dbForProject->getDocument('messages', $messageId);
if ($message->isEmpty()) {
@@ -3775,9 +3788,12 @@ App::get('/v1/messaging/messages/:messageId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'message/' . $messageId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
$output = [];
@@ -3825,7 +3841,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+6 -3
View File
@@ -21,6 +21,7 @@ use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\App;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
@@ -247,13 +248,15 @@ App::post('/v1/projects')
}
if ($create || $projectTables) {
$audit = new Audit($dbForProject);
$adapter = new AdapterDatabase($dbForProject);
$audit = new Audit($adapter);
$audit->setup();
}
if (!$create && $sharedTablesV1) {
$attributes = \array_map(fn ($attribute) => new Document($attribute), Audit::ATTRIBUTES);
$indexes = \array_map(fn (array $index) => new Document($index), Audit::INDEXES);
$adapter = new AdapterDatabase($dbForProject);
$attributes = $adapter->getAttributeDocuments();
$indexes = $adapter->getIndexDocuments();
$dbForProject->createDocument(Database::METADATA, new Document([
'$id' => ID::custom('audit'),
'$permissions' => [Permission::create(Role::any())],
+8 -4
View File
@@ -1464,7 +1464,8 @@ App::get('/v1/teams/:teamId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $teamId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->inject('audit')
->action(function (string $teamId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -1478,9 +1479,12 @@ App::get('/v1/teams/:teamId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'team/' . $team->getId();
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, offset: $offset, limit: $limit);
$output = [];
@@ -1527,7 +1531,7 @@ App::get('/v1/teams/:teamId/logs')
}
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByResource($resource, $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByResource($resource) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+8 -4
View File
@@ -945,7 +945,8 @@ App::get('/v1/users/:userId/logs')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $userId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->inject('audit')
->action(function (string $userId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit) {
$user = $dbForProject->getDocument('users', $userId);
@@ -958,8 +959,11 @@ App::get('/v1/users/:userId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$logs = $audit->getLogsByUser($user->getSequence(), $queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$logs = $audit->getLogsByUser($user->getSequence(), limit: $limit, offset: $offset);
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
@@ -999,7 +1003,7 @@ App::get('/v1/users/:userId/logs')
}
$response->dynamic(new Document([
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence(), $queries) : 0,
'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence()) : 0,
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+8 -4
View File
@@ -12,6 +12,8 @@ use Swoole\Process;
use Swoole\Table;
use Swoole\Timer;
use Utopia\App;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Adapter\SQL as AuditAdapterSQL;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
use Utopia\Compression\Compression;
@@ -260,8 +262,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
// create appwrite database, `dbForPlatform` is a direct access call.
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections) {
if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForPlatform);
if ($dbForPlatform->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
$adapter = new AdapterDatabase($dbForPlatform);
$audit = new Audit($adapter);
$audit->setup();
}
@@ -389,8 +392,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
Console::success('[Setup] - Skip: metadata table already exists');
}
if ($dbForProject->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForProject);
if ($dbForProject->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
$adapter = new AdapterDatabase($dbForProject);
$audit = new Audit($adapter);
$audit->setup();
}
+9 -2
View File
@@ -4,7 +4,7 @@ use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Key;
use Appwrite\Databases\TransactionState;
use Appwrite\Event\Audit;
use Appwrite\Event\Audit as AuditEvent;
use Appwrite\Event\Build;
use Appwrite\Event\Certificate;
use Appwrite\Event\Database as EventDatabase;
@@ -30,6 +30,8 @@ use Appwrite\Utopia\Response;
use Executor\Executor;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Audit;
use Utopia\Auth\Hashes\Argon2;
use Utopia\Auth\Hashes\Sha;
use Utopia\Auth\Proofs\Code;
@@ -146,7 +148,7 @@ App::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
App::setResource('queueForAudits', function (Publisher $publisher) {
return new Audit($publisher);
return new AuditEvent($publisher);
}, ['publisher']);
App::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
@@ -642,6 +644,11 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
};
}, ['pools', 'cache']);
App::setResource('audit', function ($dbForProject) {
$adapter = new AdapterDatabase($dbForProject);
return new Audit($adapter);
}, ['dbForProject']);
App::setResource('telemetry', fn () => new NoTelemetry());
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
+15
View File
@@ -21,6 +21,8 @@ use Appwrite\Utopia\Database\Documents\User;
use Executor\Executor;
use Swoole\Runtime;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\Audit\Adapter\Database as AdapterDatabase;
use Utopia\Audit\Audit as UtopiaAudit;
use Utopia\Cache\Adapter\Pool as CachePool;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
@@ -450,6 +452,19 @@ Server::setResource('logError', function (Registry $register, Document $project)
Server::setResource('executor', fn () => new Executor());
Server::setResource('getAudit', function (Database $dbForPlatform, callable $getProjectDB) {
return function (Document $project) use ($dbForPlatform, $getProjectDB) {
if ($project->isEmpty() || $project->getId() === 'console') {
$adapter = new AdapterDatabase($dbForPlatform);
return new UtopiaAudit($adapter);
}
$dbForProject = $getProjectDB($project);
$adapter = new AdapterDatabase($dbForProject);
return new UtopiaAudit($adapter);
};
}, ['dbForPlatform', 'getProjectDB']);
$pools = $register->get('pools');
$platform = new Appwrite();
$args = $platform->getEnv('argv');
+3 -3
View File
@@ -47,12 +47,12 @@
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "1.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "1.*",
"utopia-php/audit": "2.0.2-rc1",
"utopia-php/auth": "0.5.*",
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "1.*.*",
"utopia-php/database": "3.*",
"utopia-php/database": "3.*.*",
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "0.9.*",
"utopia-php/emails": "0.6.*",
@@ -109,4 +109,4 @@
"tbachert/spi": true
}
}
}
}
Generated
+61 -57
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7c9cb03eb5267f1e7a3ffc037ae22b6a",
"content-hash": "b873febd2b03c32ec61a57b690cc44a2",
"packages": [
{
"name": "adhocore/jwt",
@@ -3552,21 +3552,23 @@
},
{
"name": "utopia-php/audit",
"version": "1.0.2",
"version": "2.0.2-rc1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "8c17065c2473d4ca799f65585ca74eb53e1be211"
"reference": "7b35dab40bce66bda56eeeacd2bbcbf1e823f05f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/8c17065c2473d4ca799f65585ca74eb53e1be211",
"reference": "8c17065c2473d4ca799f65585ca74eb53e1be211",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/7b35dab40bce66bda56eeeacd2bbcbf1e823f05f",
"reference": "7b35dab40bce66bda56eeeacd2bbcbf1e823f05f",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "*"
"utopia-php/database": "3.*",
"utopia-php/fetch": "^0.4.2",
"utopia-php/validators": "^0.1.0"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3593,9 +3595,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/1.0.2"
"source": "https://github.com/utopia-php/audit/tree/2.0.2-rc1"
},
"time": "2025-10-20T07:14:26+00:00"
"time": "2025-12-24T01:20:43+00:00"
},
{
"name": "utopia-php/auth",
@@ -3654,16 +3656,16 @@
},
{
"name": "utopia-php/cache",
"version": "0.13.1",
"version": "0.13.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "97220cb3b3822b166ee016d1646e2ae2815dc540"
"reference": "5768498c9f451482f0bf3eede4d6452ddcd4a0f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/97220cb3b3822b166ee016d1646e2ae2815dc540",
"reference": "97220cb3b3822b166ee016d1646e2ae2815dc540",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/5768498c9f451482f0bf3eede4d6452ddcd4a0f6",
"reference": "5768498c9f451482f0bf3eede4d6452ddcd4a0f6",
"shasum": ""
},
"require": {
@@ -3672,7 +3674,7 @@
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/pools": "0.8.*",
"utopia-php/telemetry": "0.1.*"
"utopia-php/telemetry": "*"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -3700,9 +3702,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.13.1"
"source": "https://github.com/utopia-php/cache/tree/0.13.2"
},
"time": "2025-05-09T14:43:52+00:00"
"time": "2025-12-17T08:55:43+00:00"
},
{
"name": "utopia-php/cli",
@@ -3896,16 +3898,16 @@
},
{
"name": "utopia-php/database",
"version": "3.6.0",
"version": "3.6.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "af15066255a5fd7bd2926de37bcbf3d8500fc155"
"reference": "c8c1b2f5770245dd4006e2680681e3efbe8b1fa7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/af15066255a5fd7bd2926de37bcbf3d8500fc155",
"reference": "af15066255a5fd7bd2926de37bcbf3d8500fc155",
"url": "https://api.github.com/repos/utopia-php/database/zipball/c8c1b2f5770245dd4006e2680681e3efbe8b1fa7",
"reference": "c8c1b2f5770245dd4006e2680681e3efbe8b1fa7",
"shasum": ""
},
"require": {
@@ -3948,9 +3950,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/3.6.0"
"source": "https://github.com/utopia-php/database/tree/3.6.1"
},
"time": "2025-12-08T05:23:04+00:00"
"time": "2025-12-16T09:55:41+00:00"
},
{
"name": "utopia-php/detector",
@@ -3999,23 +4001,23 @@
},
{
"name": "utopia-php/dns",
"version": "1.4.0",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/dns.git",
"reference": "dce3453364a4524b7250db8d8eb74820b814409e"
"reference": "5daf8b683dad877491c4df84c6be24850b2f363b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/dce3453364a4524b7250db8d8eb74820b814409e",
"reference": "dce3453364a4524b7250db8d8eb74820b814409e",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/5daf8b683dad877491c4df84c6be24850b2f363b",
"reference": "5daf8b683dad877491c4df84c6be24850b2f363b",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/console": "0.0.*",
"utopia-php/domains": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/telemetry": "*",
"utopia-php/validators": "0.*"
},
"require-dev": {
@@ -4050,9 +4052,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/dns/issues",
"source": "https://github.com/utopia-php/dns/tree/1.4.0"
"source": "https://github.com/utopia-php/dns/tree/1.4.1"
},
"time": "2025-12-05T10:09:00+00:00"
"time": "2025-12-17T09:09:08+00:00"
},
{
"name": "utopia-php/domains",
@@ -4730,21 +4732,21 @@
},
{
"name": "utopia-php/pools",
"version": "0.8.2",
"version": "0.8.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/pools.git",
"reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d"
"reference": "ad7d6ba946376e81c603204285ce9a674b6502b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/05c67aba42eb68ac65489cc1e7fc5db83db2dd4d",
"reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/ad7d6ba946376e81c603204285ce9a674b6502b8",
"reference": "ad7d6ba946376e81c603204285ce9a674b6502b8",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/telemetry": "0.1.*"
"php": ">=8.4",
"utopia-php/telemetry": "*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -4776,9 +4778,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/pools/issues",
"source": "https://github.com/utopia-php/pools/tree/0.8.2"
"source": "https://github.com/utopia-php/pools/tree/0.8.3"
},
"time": "2025-04-17T02:04:54+00:00"
"time": "2025-12-17T09:35:18+00:00"
},
{
"name": "utopia-php/preloader",
@@ -4835,16 +4837,16 @@
},
{
"name": "utopia-php/queue",
"version": "0.11.1",
"version": "0.11.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "498bbbef418b1db71b51e1bb62f5d1d752ddd8d6"
"reference": "a854f7c4abc18e0eca55fc5608cd7088d71eb19f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/498bbbef418b1db71b51e1bb62f5d1d752ddd8d6",
"reference": "498bbbef418b1db71b51e1bb62f5d1d752ddd8d6",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/a854f7c4abc18e0eca55fc5608cd7088d71eb19f",
"reference": "a854f7c4abc18e0eca55fc5608cd7088d71eb19f",
"shasum": ""
},
"require": {
@@ -4854,7 +4856,7 @@
"utopia-php/fetch": "0.4.*",
"utopia-php/framework": "0.33.*",
"utopia-php/pools": "0.8.*",
"utopia-php/telemetry": "0.1.*"
"utopia-php/telemetry": "*"
},
"require-dev": {
"ext-redis": "*",
@@ -4895,9 +4897,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.11.1"
"source": "https://github.com/utopia-php/queue/tree/0.11.2"
},
"time": "2025-05-30T11:50:34+00:00"
"time": "2025-12-17T09:32:35+00:00"
},
{
"name": "utopia-php/registry",
@@ -4953,16 +4955,16 @@
},
{
"name": "utopia-php/storage",
"version": "0.18.16",
"version": "0.18.18",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "0c7b8ad68de8e1eb23ccc8af9f27a30eb832930f"
"reference": "acaea524f315f87b8811a2c34450fe2b502f49d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/0c7b8ad68de8e1eb23ccc8af9f27a30eb832930f",
"reference": "0c7b8ad68de8e1eb23ccc8af9f27a30eb832930f",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/acaea524f315f87b8811a2c34450fe2b502f49d8",
"reference": "acaea524f315f87b8811a2c34450fe2b502f49d8",
"shasum": ""
},
"require": {
@@ -5005,9 +5007,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.18.16"
"source": "https://github.com/utopia-php/storage/tree/0.18.18"
},
"time": "2025-12-03T02:15:45+00:00"
"time": "2025-12-17T07:33:45+00:00"
},
{
"name": "utopia-php/swoole",
@@ -5436,16 +5438,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.5.9",
"version": "1.7.2",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93"
"reference": "3876d486e2c00b788fbda677ef9fcc77391b8898"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ee434aa00a9185380b9a39bb46bf86d7104d3a93",
"reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/3876d486e2c00b788fbda677ef9fcc77391b8898",
"reference": "3876d486e2c00b788fbda677ef9fcc77391b8898",
"shasum": ""
},
"require": {
@@ -5481,9 +5483,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.9"
"source": "https://github.com/appwrite/sdk-generator/tree/1.7.2"
},
"time": "2025-11-25T05:22:25+00:00"
"time": "2025-12-24T07:49:12+00:00"
},
{
"name": "doctrine/annotations",
@@ -8943,7 +8945,9 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"utopia-php/audit": 5
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -8967,5 +8971,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.9.0"
}
@@ -72,10 +72,11 @@ class XList extends Action
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('audit')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
@@ -98,12 +99,16 @@ class XList extends Action
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$type = $this->getCollectionsEventsContext();
$context = $this->getContext();
$resource = "database/$databaseId/$type/$collectionId/$context/{$document->getId()}";
$logs = $audit->getLogsByResource($resource, $queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
$output = [];
@@ -152,7 +157,7 @@ class XList extends Action
$response->dynamic(new Document([
'logs' => $output,
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
]), $this->getResponseModel());
}
}
@@ -71,10 +71,11 @@ class XList extends Action
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('audit')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -95,10 +96,13 @@ class XList extends Action
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$context = $this->getContext();
$resource = "database/$databaseId/$context/$collectionId";
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
$output = [];
@@ -147,7 +151,7 @@ class XList extends Action
$response->dynamic(new Document([
'logs' => $output,
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
]), $this->getResponseModel());
}
}
@@ -67,10 +67,11 @@ class XList extends Action
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('audit')
->callback($this->action(...));
}
public function action(string $databaseId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void
public function action(string $databaseId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit): void
{
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -84,9 +85,13 @@ class XList extends Action
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'database/' . $databaseId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
$output = [];
@@ -133,7 +138,7 @@ class XList extends Action
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), UtopiaResponse::MODEL_LOG_LIST);
}
@@ -62,10 +62,11 @@ class XList extends Action
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('audit')
->callback($this->action(...));
}
public function action(string $databaseId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb): void
public function action(string $databaseId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit): void
{
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -79,9 +80,12 @@ class XList extends Action
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$audit = new Audit($dbForProject);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$resource = 'database/' . $databaseId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, limit: $limit, offset: $offset);
$output = [];
@@ -128,7 +132,7 @@ class XList extends Action
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), UtopiaResponse::MODEL_LOG_LIST);
}
@@ -50,6 +50,7 @@ class XList extends CollectionLogXList
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('audit')
->callback($this->action(...));
}
}
@@ -51,6 +51,7 @@ class XList extends DocumentLogXList
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('audit')
->callback($this->action(...));
}
}
+5 -6
View File
@@ -4,7 +4,6 @@ namespace Appwrite\Platform\Workers;
use Exception;
use Throwable;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization;
@@ -42,8 +41,8 @@ class Audits extends Action
$this
->desc('Audits worker')
->inject('message')
->inject('getProjectDB')
->inject('project')
->inject('getAudit')
->callback($this->action(...));
$this->lastTriggeredTime = time();
@@ -54,13 +53,14 @@ class Audits extends Action
* @param Message $message
* @param callable $getProjectDB
* @param Document $project
* @param callable $getAudit
* @return Commit|NoCommit
* @throws Throwable
* @throws \Utopia\Database\Exception
* @throws Authorization
* @throws Structure
*/
public function action(Message $message, callable $getProjectDB, Document $project): Commit|NoCommit
public function action(Message $message, Document $project, callable $getAudit): Commit|NoCommit
{
$payload = $message->getPayload() ?? [];
@@ -102,7 +102,7 @@ class Audits extends Action
'mode' => $mode,
'data' => $auditPayload,
],
'timestamp' => date("Y-m-d H:i:s", $message->getTimestamp()),
'time' => date("Y-m-d H:i:s", $message->getTimestamp()),
];
if (isset($this->logs[$project->getSequence()])) {
@@ -135,8 +135,7 @@ class Audits extends Action
Console::log('Processing Project "' . $sequence . '" batch with ' . count($projectLogs['logs']) . ' events');
$projectDocument = $projectLogs['project'];
$dbForProject = $getProjectDB($projectDocument);
$audit = new Audit($dbForProject);
$audit = $getAudit($projectDocument);
$audit->logBatch($projectLogs['logs']);
Console::success('Audit logs processed successfully');
+13 -13
View File
@@ -9,6 +9,7 @@ use Appwrite\Extend\Exception;
use Executor\Executor;
use Throwable;
use Utopia\Abuse\Adapters\TimeLimit\Database as AbuseDatabase;
use Utopia\Audit\Adapter\SQL;
use Utopia\Audit\Audit;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
@@ -62,6 +63,7 @@ class Deletes extends Action
->inject('executionRetention')
->inject('auditRetention')
->inject('log')
->inject('getAudit')
->callback($this->action(...));
}
@@ -84,7 +86,8 @@ class Deletes extends Action
Executor $executor,
string $executionRetention,
string $auditRetention,
Log $log
Log $log,
callable $getAudit,
): void {
$payload = $message->getPayload() ?? [];
@@ -145,7 +148,7 @@ class Deletes extends Action
break;
case DELETE_TYPE_AUDIT:
if (!$project->isEmpty()) {
$this->deleteAuditLogs($project, $getProjectDB, $auditRetention);
$this->deleteAuditLogs($project, $auditRetention, $getAudit);
}
break;
case DELETE_TYPE_REALTIME:
@@ -517,7 +520,7 @@ class Deletes extends Action
$projectCollectionIds = [
...\array_keys(Config::getParam('collections', [])['projects']),
Audit::COLLECTION,
SQL::COLLECTION,
AbuseDatabase::COLLECTION,
];
@@ -783,23 +786,20 @@ class Deletes extends Action
* @param Database $dbForPlatform
* @param callable $getProjectDB
* @param string $auditRetention
* @param callable $getAudit
* @return void
* @throws Exception
*/
private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention): void
private function deleteAuditLogs(Document $project, string $auditRetention, callable $getAudit): void
{
$projectId = $project->getId();
$dbForProject = $getProjectDB($project);
/** @var Audit $audit */
$audit = $getAudit($project);
try {
$this->deleteByGroup(Audit::COLLECTION, [
Query::select([...$this->selects, 'time']),
Query::lessThan('time', $auditRetention),
Query::orderDesc('time'),
Query::orderAsc(),
], $dbForProject);
} catch (DatabaseException $e) {
Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage());
$audit->cleanup(new \DateTime($auditRetention));
} catch (Throwable $th) {
Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $th->getMessage());
}
}