diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 36311e8461..d6b99f8855 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -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); }); diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index a2eefb3355..0b6a314dc5 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -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); }); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index a49f594301..49c0003588 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -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())], diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 8771588d3a..1f8555b6cd 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -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); }); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 36df3d7a90..bbe1d8a84a 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -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); }); diff --git a/app/http.php b/app/http.php index 1bd3e97e69..b7f857da48 100644 --- a/app/http.php +++ b/app/http.php @@ -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(); } diff --git a/app/init/resources.php b/app/init/resources.php index 30717141f6..d56354c14b 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -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) { diff --git a/app/worker.php b/app/worker.php index 76f3bb9e8a..7868861cf4 100644 --- a/app/worker.php +++ b/app/worker.php @@ -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'); diff --git a/composer.json b/composer.json index d32b739311..844a10d7e8 100644 --- a/composer.json +++ b/composer.json @@ -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 } } -} +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index 4ffc7e7db4..f637488a9a 100644 --- a/composer.lock +++ b/composer.lock @@ -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" } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 47f5247831..a4dd38ef67 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -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()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index a45daa32a4..0f5a57c6e9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -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()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php index a794ec325e..319f07db1c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php @@ -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); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php index 53476dbae1..88745555d9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php @@ -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); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php index 0680649544..5eab050b7e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php @@ -50,6 +50,7 @@ class XList extends CollectionLogXList ->inject('dbForProject') ->inject('locale') ->inject('geodb') + ->inject('audit') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php index 5f1efa2953..27bd82195d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php @@ -51,6 +51,7 @@ class XList extends DocumentLogXList ->inject('dbForProject') ->inject('locale') ->inject('geodb') + ->inject('audit') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index be542e7811..91dbb7cb00 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -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'); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index dfd9aebbf5..5c36fa7f7a 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -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()); } }