From 965c4738e7701ddcc6ad819e8d554126f59ace89 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 5 Nov 2025 05:06:40 +0000 Subject: [PATCH] fix user's attributes --- app/controllers/api/users.php | 67 +++++++++++------------------------ 1 file changed, 20 insertions(+), 47 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index f3a76f2404..bdde62dffa 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -642,9 +642,10 @@ App::get('/v1/users') )) ->param('queries', [], new Users(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') - ->action(function (array $queries, string $search, Response $response, Database $dbForProject) { + ->action(function (array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject) { try { $queries = Query::parseQueries($queries); @@ -684,10 +685,10 @@ App::get('/v1/users') $users = []; $total = 0; - $dbForProject->skipFilters(function () use ($dbForProject, $queries, &$users, &$total) { + $dbForProject->skipFilters(function () use ($dbForProject, $queries, $includeTotal, &$users, &$total) { try { $users = $dbForProject->find('users', $queries); - $total = $dbForProject->count('users', $queries, APP_LIMIT_COUNT); + $total = $includeTotal ? $dbForProject->count('users', $queries, APP_LIMIT_COUNT) : 0; } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } catch (QueryException $e) { @@ -821,32 +822,29 @@ App::get('/v1/users/:userId/sessions') ] )) ->param('userId', '', new UID(), 'User ID.') + ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') ->inject('locale') - ->action(function (string $userId, Response $response, Database $dbForProject, Locale $locale) { + ->action(function (string $userId, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale) { $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - $sessions = $user->getAttribute('sessions', []); - foreach ($sessions as $key => $session) { /** @var Document $session */ - $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session->setAttribute('countryName', $countryName); $session->setAttribute('current', false); - $sessions[$key] = $session; } $response->dynamic(new Document([ 'sessions' => $sessions, - 'total' => count($sessions), + 'total' => $includeTotal ? count($sessions) : 0, ]), Response::MODEL_SESSION_LIST); }); @@ -870,43 +868,38 @@ App::get('/v1/users/:userId/memberships') ->param('userId', '', new UID(), 'User ID.') ->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $userId, array $queries, string $search, Response $response, Database $dbForProject) { + ->action(function (string $userId, array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject) { $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - if (!empty($search)) { $queries[] = Query::search('search', $search); } - // Set internal queries $queries[] = Query::equal('userInternalId', [$user->getSequence()]); - $memberships = array_map(function ($membership) use ($dbForProject, $user) { $team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId')); - $membership ->setAttribute('teamName', $team->getAttribute('name')) ->setAttribute('userName', $user->getAttribute('name')) ->setAttribute('userEmail', $user->getAttribute('email')); - return $membership; }, $dbForProject->find('memberships', $queries)); $response->dynamic(new Document([ 'memberships' => $memberships, - 'total' => count($memberships), + 'total' => $includeTotal ? count($memberships) : 0, ]), Response::MODEL_MEMBERSHIP_LIST); }); @@ -929,46 +922,38 @@ App::get('/v1/users/:userId/logs') )) ->param('userId', '', new UID(), 'User ID.') ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) + ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->action(function (string $userId, array $queries, bool $includeTotal, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - // Temp fix for logs $queries[] = Query::or([ Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), ]); - $audit = new Audit($dbForProject); - $logs = $audit->getLogsByUser($user->getSequence(), $queries); - $output = []; - foreach ($logs as $i => &$log) { $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - $detector = new Detector($log['userAgent']); $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - $os = $detector->getOS(); $client = $detector->getClient(); $device = $detector->getDevice(); - $output[$i] = new Document([ 'event' => $log['event'], 'userId' => ID::custom($log['data']['userId']), @@ -989,9 +974,7 @@ App::get('/v1/users/:userId/logs') 'deviceBrand' => $device['deviceBrand'], 'deviceModel' => $device['deviceModel'] ]); - $record = $geodb->get($log['ip']); - if ($record) { $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); @@ -1002,7 +985,7 @@ App::get('/v1/users/:userId/logs') } $response->dynamic(new Document([ - 'total' => $audit->countLogsByUser($user->getSequence(), $queries), + 'total' => $includeTotal ? $audit->countLogsByUser($user->getSequence(), $queries) : 0, 'logs' => $output, ]), Response::MODEL_LOG_LIST); }); @@ -1026,23 +1009,21 @@ App::get('/v1/users/:userId/targets') )) ->param('userId', '', new UID(), 'User ID.') ->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Targets::ALLOWED_ATTRIBUTES), true) + ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $userId, array $queries, Response $response, Database $dbForProject) { + ->action(function (string $userId, array $queries, bool $includeTotal, Response $response, Database $dbForProject) { $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $queries[] = Query::equal('userId', [$userId]); - /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries */ @@ -1050,25 +1031,21 @@ App::get('/v1/users/:userId/targets') return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); }); $cursor = reset($cursor); - if ($cursor) { $validator = new Cursor(); if (!$validator->isValid($cursor)) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $targetId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('targets', $targetId); - if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Target '{$targetId}' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument); } try { $targets = $dbForProject->find('targets', $queries); - $total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT); + $total = $includeTotal ? $dbForProject->count('targets', $queries, APP_LIMIT_COUNT) : 0; } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } @@ -1097,19 +1074,20 @@ App::get('/v1/users/identities') )) ->param('queries', [], new Identities(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Identities::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') - ->action(function (array $queries, string $search, Response $response, Database $dbForProject) { + ->action(function (array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } + if (!empty($search)) { $queries[] = Query::search('search', $search); } - /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries */ @@ -1119,26 +1097,21 @@ App::get('/v1/users/identities') $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ - $validator = new Cursor(); if (!$validator->isValid($cursor)) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $identityId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('identities', $identityId); - if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$identityId}' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument); } - $filterQueries = Query::groupByType($queries)['filters']; try { $identities = $dbForProject->find('identities', $queries); - $total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT); + $total = $includeTotal ? $dbForProject->count('identities', $queries, APP_LIMIT_COUNT) : 0; } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); }