mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a66b565150 | ||
|
|
ff160871ae | ||
|
|
92a3eca194 | ||
|
|
07135956df | ||
|
|
b5a363a2a7 | ||
|
|
31dc9ff873 | ||
|
|
408f9c2d39 | ||
|
|
dfd5965d25 | ||
|
|
2a38eedc98 | ||
|
|
f875b965be | ||
|
|
2e57e5a868 | ||
|
|
69ce99d1b4 | ||
|
|
6b881d4aaf | ||
|
|
aad7d6dbca | ||
|
|
e3412bc554 | ||
|
|
15dc316535 | ||
|
|
a7724028b3 | ||
|
|
419d56b4b1 | ||
|
|
d2cda9770b | ||
|
|
69430154e6 | ||
|
|
0601305545 | ||
|
|
5519086c29 | ||
|
|
33b9f33f44 | ||
|
|
8baa5cb659 | ||
|
|
cec5cd7bdc | ||
|
|
63c2d72b2e | ||
|
|
801219374c | ||
|
|
dc3459edd0 | ||
|
|
ace9d86744 | ||
|
|
2d3e34fd5a | ||
|
|
69ad4ae930 | ||
|
|
0037305e16 | ||
|
|
1a205c3f3b | ||
|
|
99966b5a24 | ||
|
|
2437d2be77 | ||
|
|
2043470bd0 | ||
|
|
667285d536 | ||
|
|
1199c1fc52 | ||
|
|
f270e47b48 | ||
|
|
5678860d13 | ||
|
|
b83125a41e | ||
|
|
2dcb131778 | ||
|
|
30083598c6 | ||
|
|
a0599d2658 | ||
|
|
c50db111d6 | ||
|
|
9441295510 |
@@ -58,10 +58,6 @@ Examples:
|
||||
|
||||
Avoid introducing new dependencies other than utopia-php.
|
||||
|
||||
## Adding new endpoints
|
||||
|
||||
When adding new endpoints, make sure to use modules and follow its patterns. Find instruction in [Modules AGENTS.md](src/Appwrite/Platform/AGENTS.md) file.
|
||||
|
||||
## Pull Request Guidelines
|
||||
### Before Submitting
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ return [
|
||||
'name' => 'Console',
|
||||
'subtitle' => '',
|
||||
'description' => '',
|
||||
'controller' => 'web/console.php',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => false,
|
||||
'docs' => false,
|
||||
'docsUrl' => '',
|
||||
@@ -270,9 +270,9 @@ return [
|
||||
'console' => [
|
||||
'key' => 'console',
|
||||
'name' => 'Console',
|
||||
'subtitle' => 'The Console service allows you to interact with console relevant informations.',
|
||||
'subtitle' => 'The Console service allows you to interact with console relevant information.',
|
||||
'description' => '',
|
||||
'controller' => 'api/console.php',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => '',
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\IP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
App::init()
|
||||
->groups(['console'])
|
||||
->inject('project')
|
||||
->action(function (Document $project) {
|
||||
if ($project->getId() !== 'console') {
|
||||
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
App::get('/v1/console/variables')
|
||||
->desc('Get variables')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
group: 'console',
|
||||
name: 'variables',
|
||||
description: '/docs/references/console/variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_CONSOLE_VARIABLES,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->inject('response')
|
||||
->action(function (Response $response) {
|
||||
$validator = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME'));
|
||||
$isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest();
|
||||
|
||||
$validator = new IP(IP::V4);
|
||||
$isAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_A', '')) && ($validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_A')));
|
||||
|
||||
$validator = new IP(IP::V6);
|
||||
$isAAAAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_AAAA', '')) && $validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA'));
|
||||
|
||||
$isDomainEnabled = $isAAAAValid || $isAValid || $isCNAMEValid;
|
||||
|
||||
$isVcsEnabled = !empty(System::getEnv('_APP_VCS_GITHUB_APP_NAME', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_APP_ID', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''));
|
||||
|
||||
$isAssistantEnabled = !empty(System::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', ''));
|
||||
|
||||
$variables = new Document([
|
||||
'_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'),
|
||||
'_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'),
|
||||
'_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'),
|
||||
// Combine CAA domain with most common flags and tag (no parameters)
|
||||
'_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"',
|
||||
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
|
||||
'_APP_COMPUTE_BUILD_TIMEOUT' => +System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT'),
|
||||
'_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'),
|
||||
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
|
||||
'_APP_VCS_ENABLED' => $isVcsEnabled,
|
||||
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
|
||||
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled,
|
||||
'_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'),
|
||||
'_APP_DOMAIN_FUNCTIONS' => System::getEnv('_APP_DOMAIN_FUNCTIONS'),
|
||||
'_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'),
|
||||
'_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'),
|
||||
]);
|
||||
|
||||
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
||||
});
|
||||
|
||||
App::post('/v1/console/assistant')
|
||||
->desc('Create assistant query')
|
||||
->groups(['api', 'assistant'])
|
||||
->label('scope', 'assistant.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'assistant',
|
||||
group: 'console',
|
||||
name: 'chat',
|
||||
description: '/docs/references/assistant/chat.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::TEXT
|
||||
))
|
||||
->label('abuse-limit', 15)
|
||||
->label('abuse-key', 'userId:{userId}')
|
||||
->param('prompt', '', new Text(2000), 'Prompt. A string containing questions asked to the AI assistant.')
|
||||
->inject('response')
|
||||
->action(function (string $prompt, Response $response) {
|
||||
$ch = curl_init('http://appwrite-assistant:3003/v1/models/assistant/prompt');
|
||||
$responseHeaders = [];
|
||||
$query = json_encode(['prompt' => $prompt]);
|
||||
$headers = ['accept: text/event-stream'];
|
||||
$handleEvent = function ($ch, $data) use ($response) {
|
||||
$response->chunk($data);
|
||||
|
||||
return \strlen($data);
|
||||
};
|
||||
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $handleEvent);
|
||||
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 9000);
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
|
||||
$len = strlen($header);
|
||||
$header = explode(':', $header, 2);
|
||||
|
||||
if (count($header) < 2) { // ignore invalid headers
|
||||
return $len;
|
||||
}
|
||||
|
||||
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
|
||||
|
||||
return $len;
|
||||
});
|
||||
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
|
||||
|
||||
curl_exec($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
$response->chunk('', true);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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())],
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
|
||||
App::init()
|
||||
->groups(['web'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$response
|
||||
->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
|
||||
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI()))
|
||||
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/')
|
||||
->alias('auth/*')
|
||||
->alias('/invite')
|
||||
->alias('/login')
|
||||
->alias('/mfa')
|
||||
->alias('/card/*')
|
||||
->alias('/recover')
|
||||
->alias('/register/*')
|
||||
->groups(['web'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$url = parse_url($request->getURI());
|
||||
$target = "/console{$url['path']}";
|
||||
$params = $request->getParams();
|
||||
if (!empty($params)) {
|
||||
$target .= "?" . \http_build_query($params);
|
||||
}
|
||||
if ($url['fragment'] ?? false) {
|
||||
$target .= "#{$url['fragment']}";
|
||||
}
|
||||
$response->redirect($target);
|
||||
});
|
||||
+8
-4
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -11,6 +11,7 @@ $httpsPort = $this->getParam('httpsPort', '');
|
||||
$version = $this->getParam('version', '');
|
||||
$organization = $this->getParam('organization', '');
|
||||
$image = $this->getParam('image', '');
|
||||
$enableAssistant = $this->getParam('enableAssistant', false);
|
||||
?>services:
|
||||
traefik:
|
||||
image: traefik:2.11
|
||||
@@ -848,6 +849,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
|
||||
<?php if ($enableAssistant): ?>
|
||||
appwrite-assistant:
|
||||
image: appwrite/assistant:0.8.4
|
||||
container_name: appwrite-assistant
|
||||
@@ -857,6 +859,7 @@ $image = $this->getParam('image', '');
|
||||
- appwrite
|
||||
environment:
|
||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||
<?php endif; ?>
|
||||
|
||||
appwrite-browser:
|
||||
image: appwrite/browser:0.3.2
|
||||
|
||||
@@ -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
@@ -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
+79
-75
@@ -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",
|
||||
@@ -2673,16 +2673,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.4.1",
|
||||
"version": "v7.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "26cc224ea7103dda90e9694d9e139a389092d007"
|
||||
"reference": "d01dfac1e0dc99f18da48b18101c23ce57929616"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/26cc224ea7103dda90e9694d9e139a389092d007",
|
||||
"reference": "26cc224ea7103dda90e9694d9e139a389092d007",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/d01dfac1e0dc99f18da48b18101c23ce57929616",
|
||||
"reference": "d01dfac1e0dc99f18da48b18101c23ce57929616",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2750,7 +2750,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.4.1"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.4.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2770,7 +2770,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-04T21:12:57+00:00"
|
||||
"time": "2025-12-23T14:50:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@@ -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.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93"
|
||||
"reference": "b6cc29d3bd247e193f3c06b4168dc69d884645f0"
|
||||
},
|
||||
"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/b6cc29d3bd247e193f3c06b4168dc69d884645f0",
|
||||
"reference": "b6cc29d3bd247e193f3c06b4168dc69d884645f0",
|
||||
"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.8.6"
|
||||
},
|
||||
"time": "2025-11-25T05:22:25+00:00"
|
||||
"time": "2025-12-31T10:22:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
@@ -7931,16 +7933,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.0.1",
|
||||
"version": "v8.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "fcb73f69d655b48fcb894a262f074218df08bd58"
|
||||
"reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/fcb73f69d655b48fcb894a262f074218df08bd58",
|
||||
"reference": "fcb73f69d655b48fcb894a262f074218df08bd58",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/6145b304a5c1ea0bdbd0b04d297a5864f9a7d587",
|
||||
"reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7997,7 +7999,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.1"
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8017,7 +8019,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-05T15:25:33+00:00"
|
||||
"time": "2025-12-23T14:52:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
@@ -8091,16 +8093,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v8.0.0",
|
||||
"version": "v8.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291"
|
||||
"reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/7598dd5770580fa3517ec83e8da0c9b9e01f4291",
|
||||
"reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12",
|
||||
"reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -8135,7 +8137,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v8.0.0"
|
||||
"source": "https://github.com/symfony/finder/tree/v8.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8155,7 +8157,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-05T14:36:47+00:00"
|
||||
"time": "2025-12-23T14:52:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# Modules AGENTS.md
|
||||
|
||||
> Before reading this file, also read Appwrite's base [AGENTS.md](../../../AGENTS.md).
|
||||
|
||||
Modules are the building blocks of the Appwrite platform. They are responsible for handling specific tasks, defining background workers, and providing API endpoints. Each module should have its own directory within the `src/Appwrite/Platform/Modules` directory.
|
||||
|
||||
Generally-speaking, each service is its own module, but there are some exceptions. The goal is to always put related code that achieves a specific goal under one roof.
|
||||
|
||||
## Structure and Naming Conventions
|
||||
|
||||
When adding a module, always add a new directory under `src/Appwrite/Platform/Modules`. The directory name should be PascalCase, and if possible, use only one word. For example, `User`, `Database`, `Storage`, etc. Avoid using shorthands, unless they are standardized, such as `DB`, `JWT`, or `SMTP`.
|
||||
|
||||
A module consists of:
|
||||
|
||||
- `Module.php` - Simple register class registering all module's services (from `Services` directory)
|
||||
- `Workers` directory - Contains behavior for module-specific workers
|
||||
- `Tasks` directory - Contains behavior for module-specific CLI tasks
|
||||
- `Http` directory - Contains HTTP endpoints for the module
|
||||
- `Services` directory - Contains register classes for all relevant types of services
|
||||
|
||||
Inside module, the `Services` directory can contain:
|
||||
|
||||
- `Http.php` - Register HTTP endpoints and hooks from `Http` directory
|
||||
- `Workers.php` - Register workers from `Workers` directory
|
||||
- `Tasks.php` - Register CLI tasks from `Tasks` directory
|
||||
|
||||
> After implementing a module, make sure to register it in `src/Appwrite/Platform/Appwrite.php`.
|
||||
|
||||
### HTTP directory structure
|
||||
|
||||
Inside module's `Http` directory, there are multiple rules to follow:
|
||||
|
||||
1. Directly in `Http` directory, there should only be directories for services (and hooks, check point number 2). If a module is a single service, it's okay to only have one directory, with the same name as the service, for example `src/Appwrite/Platform/Modules/Account/Http/Account`. An example with multiple services is `src/Appwrite/Platform/Modules/Databases/Http/Databases` and `src/Appwrite/Platform/Modules/Databases/Http/TablesDB`.
|
||||
|
||||
2. Hooks should live in `Hooks` directory, under `Init`, `Shutdown`, or `Error` directories, inside `Http` directory. For example, an init hook to prevent unauthorized access might live in `src/Appwrite/Platform/Modules/Functions/Http/Hooks/Init/Authentication.php`.
|
||||
|
||||
3. Inside `Http` directories for services, file names can only be `Get.php`, `Update.php`, `Create.php`, `Delete.php` or `XList.php`. We call it `XList`, because `List` is a reserved keyword and PHP would not like that. Never use any other words! Let's say you want a method to be `blockUser`, tempting to add `Users/Block.php`, instead, think of the resource and property it affects. Better naming would be `Users/Status/Update.php` (update user's status). Doing so also nicely reflects in the HTTP endpoint, `PATCH /v1/users/:userId/status`.
|
||||
|
||||
4. It's allowed to nest directories in `Http` service directories. For example, if you want to create a new deployment for a function based on a template, an endpoint might live in `src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployments/Template/Create.php`. In this example, notice functions and deployments are resources, and template is property - both resources and properties can be nested, and have separate directories.
|
||||
|
||||
### Sample module directory structure
|
||||
|
||||
```bash
|
||||
src/Appwrite/Platform/Modules/Functions
|
||||
├── Module.php
|
||||
├── Workers
|
||||
│ └── Builds.php
|
||||
├── Tasks
|
||||
│ └── Block.php
|
||||
├── Http
|
||||
│ └── Functions
|
||||
│ ├── Create.php
|
||||
│ ├── XList.php
|
||||
│ ├── Update.php
|
||||
│ ├── Delete.php
|
||||
│ ├── Get.php
|
||||
│ └── Deployments
|
||||
│ ├── XList.php
|
||||
│ ├── Delete.php
|
||||
│ ├── Get.php
|
||||
│ └── Template
|
||||
│ └── Create.php
|
||||
└── Services
|
||||
├── Http.php
|
||||
├── Workers.php
|
||||
└── Tasks.php
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Assistant;
|
||||
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createAssistantQuery';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/console/assistant')
|
||||
->desc('Create assistant query')
|
||||
->groups(['api', 'assistant'])
|
||||
->label('scope', 'assistant.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'assistant',
|
||||
group: 'console',
|
||||
name: 'chat',
|
||||
description: '/docs/references/assistant/chat.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::TEXT
|
||||
))
|
||||
->label('abuse-limit', 15)
|
||||
->label('abuse-key', 'userId:{userId}')
|
||||
->param('prompt', '', new Text(2000), 'Prompt. A string containing questions asked to the AI assistant.')
|
||||
->inject('response')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $prompt, Response $response)
|
||||
{
|
||||
$ch = curl_init('http://appwrite-assistant:3003/v1/models/assistant/prompt');
|
||||
$responseHeaders = [];
|
||||
$query = json_encode(['prompt' => $prompt]);
|
||||
$headers = ['accept: text/event-stream'];
|
||||
$handleEvent = function ($ch, $data) use ($response) {
|
||||
$response->chunk($data);
|
||||
|
||||
return \strlen($data);
|
||||
};
|
||||
|
||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $handleEvent);
|
||||
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 9000);
|
||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
|
||||
$len = strlen($header);
|
||||
$header = explode(':', $header, 2);
|
||||
|
||||
if (count($header) < 2) { // ignore invalid headers
|
||||
return $len;
|
||||
}
|
||||
|
||||
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
|
||||
|
||||
return $len;
|
||||
});
|
||||
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
|
||||
|
||||
curl_exec($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
$response->chunk('', true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Init;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Action;
|
||||
|
||||
class API extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleAPI';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setType(Action::TYPE_INIT)
|
||||
->groups(['console'])
|
||||
->inject('project')
|
||||
->callback(function (Document $project) {
|
||||
if ($project->getId() !== 'console') {
|
||||
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Init;
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Platform\Action;
|
||||
|
||||
class Web extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleWeb';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setType(Action::TYPE_INIT)
|
||||
->groups(['web'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->callback(function (Request $request, Response $response) {
|
||||
$response
|
||||
->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
|
||||
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI()))
|
||||
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
|
||||
;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Auth;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectAuth';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/auth/*';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects;
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
abstract class Base extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
/**
|
||||
* HTTP platform trait doesn't support multiple `aliases`
|
||||
* like legacy controllers so we use independent redirects!
|
||||
*
|
||||
* This helps as a base and a small code logic for maintenance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getPath(): string;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath($this->getPath())
|
||||
->groups(['web'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(Request $request, Response $response): void
|
||||
{
|
||||
$url = parse_url($request->getURI());
|
||||
$target = "/console{$url['path']}";
|
||||
$params = $request->getParams();
|
||||
if (!empty($params)) {
|
||||
$target .= "?" . \http_build_query($params);
|
||||
}
|
||||
if ($url['fragment'] ?? false) {
|
||||
$target .= "#{$url['fragment']}";
|
||||
}
|
||||
|
||||
$response->redirect($target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Card;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectCard';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/card/*';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Invite;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectInvite';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/invite';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Login;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectLogin';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/login';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\MFA;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectMFA';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/mfa';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Recover;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectRecover';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/recover';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Register;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectRegister';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/register/*';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Redirects\Root;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Base;
|
||||
|
||||
class Get extends Base
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'consoleRedirectRoot';
|
||||
}
|
||||
|
||||
protected function getPath(): string
|
||||
{
|
||||
return '/';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Variables;
|
||||
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\IP;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'getVariables';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/console/variables')
|
||||
->desc('Get variables')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
group: 'console',
|
||||
name: 'variables',
|
||||
description: '/docs/references/console/variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_CONSOLE_VARIABLES,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->inject('response')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(Response $response)
|
||||
{
|
||||
$validator = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME'));
|
||||
$isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest();
|
||||
|
||||
$validator = new IP(IP::V4);
|
||||
$isAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_A', '')) && ($validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_A')));
|
||||
|
||||
$validator = new IP(IP::V6);
|
||||
$isAAAAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_AAAA', '')) && $validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA'));
|
||||
|
||||
$isDomainEnabled = $isAAAAValid || $isAValid || $isCNAMEValid;
|
||||
|
||||
$isVcsEnabled = !empty(System::getEnv('_APP_VCS_GITHUB_APP_NAME', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_APP_ID', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''))
|
||||
&& !empty(System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''));
|
||||
|
||||
$isAssistantEnabled = !empty(System::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', ''));
|
||||
|
||||
$variables = new Document([
|
||||
'_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'),
|
||||
'_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'),
|
||||
'_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'),
|
||||
'_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"',
|
||||
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
|
||||
'_APP_COMPUTE_BUILD_TIMEOUT' => +System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT'),
|
||||
'_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'),
|
||||
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
|
||||
'_APP_VCS_ENABLED' => $isVcsEnabled,
|
||||
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
|
||||
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled,
|
||||
'_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'),
|
||||
'_APP_DOMAIN_FUNCTIONS' => System::getEnv('_APP_DOMAIN_FUNCTIONS'),
|
||||
'_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'),
|
||||
'_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'),
|
||||
]);
|
||||
|
||||
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,19 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Services;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Assistant\Create as CreateAssistantQuery;
|
||||
use Appwrite\Platform\Modules\Console\Http\Init\API;
|
||||
use Appwrite\Platform\Modules\Console\Http\Init\Web;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Auth\Get as RedirectAuth;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Card\Get as RedirectCard;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Invite\Get as RedirectInvite;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Login\Get as RedirectLogin;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\MFA\Get as RedirectMFA;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Recover\Get as RedirectRecover;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Register\Get as RedirectRegister;
|
||||
use Appwrite\Platform\Modules\Console\Http\Redirects\Root\Get as RedirectRoot;
|
||||
use Appwrite\Platform\Modules\Console\Http\Resources\Get as GetResourceAvailability;
|
||||
use Appwrite\Platform\Modules\Console\Http\Variables\Get as GetVariables;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
class Http extends Service
|
||||
@@ -10,7 +22,23 @@ class Http extends Service
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = Service::TYPE_HTTP;
|
||||
// Resources
|
||||
|
||||
// API and Web init hooks!
|
||||
$this->addAction(API::getName(), new API());
|
||||
$this->addAction(Web::getName(), new Web());
|
||||
|
||||
$this->addAction(GetVariables::getName(), new GetVariables());
|
||||
$this->addAction(CreateAssistantQuery::getName(), new CreateAssistantQuery());
|
||||
$this->addAction(GetResourceAvailability::getName(), new GetResourceAvailability());
|
||||
|
||||
// web redirects to /console
|
||||
$this->addAction(RedirectRoot::getName(), new RedirectRoot());
|
||||
$this->addAction(RedirectAuth::getName(), new RedirectAuth());
|
||||
$this->addAction(RedirectInvite::getName(), new RedirectInvite());
|
||||
$this->addAction(RedirectLogin::getName(), new RedirectLogin());
|
||||
$this->addAction(RedirectMFA::getName(), new RedirectMFA());
|
||||
$this->addAction(RedirectCard::getName(), new RedirectCard());
|
||||
$this->addAction(RedirectRecover::getName(), new RedirectRecover());
|
||||
$this->addAction(RedirectRegister::getName(), new RedirectRegister());
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -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(...));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,11 +148,63 @@ class Install extends Action
|
||||
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort;
|
||||
}
|
||||
|
||||
$enableAssistant = false;
|
||||
$assistantExistsInOldCompose = false;
|
||||
|
||||
if ($data !== false && isset($compose)) {
|
||||
try {
|
||||
$assistantService = $compose->getService('appwrite-assistant');
|
||||
$assistantExistsInOldCompose = $assistantService !== null;
|
||||
} catch (\Throwable) {
|
||||
// assistant service doesn't exist, keep default false
|
||||
}
|
||||
}
|
||||
|
||||
if ($interactive == 'Y' && Console::isInteractive()) {
|
||||
$prompt = 'Add Appwrite Assistant? (Y/n)' . ($assistantExistsInOldCompose ? ' [Currently enabled]' : '');
|
||||
$answer = Console::confirm($prompt);
|
||||
|
||||
if (empty($answer)) {
|
||||
$enableAssistant = $assistantExistsInOldCompose;
|
||||
} else {
|
||||
$enableAssistant = \strtolower($answer) === 'y';
|
||||
}
|
||||
} elseif ($assistantExistsInOldCompose) {
|
||||
$enableAssistant = true;
|
||||
}
|
||||
|
||||
$input = [];
|
||||
|
||||
$password = new Password();
|
||||
$token = new Token();
|
||||
foreach ($vars as $var) {
|
||||
if ($var['name'] === '_APP_ASSISTANT_OPENAI_API_KEY') {
|
||||
if (!$enableAssistant) {
|
||||
$input[$var['name']] = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// key already exists
|
||||
if (!empty($var['default'])) {
|
||||
$input[$var['name']] = $var['default'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// if assistant enabled and no key, ask for it
|
||||
if (Console::isInteractive() && $interactive === 'Y') {
|
||||
$input[$var['name']] = Console::confirm('Enter your OpenAI API key for Appwrite Assistant:');
|
||||
if (empty($input[$var['name']])) {
|
||||
Console::warning('No API key provided. Assistant will be disabled.');
|
||||
$enableAssistant = false;
|
||||
$input[$var['name']] = '';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$input[$var['name']] = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) {
|
||||
if ($data && $var['default'] !== null) {
|
||||
$input[$var['name']] = $var['default'];
|
||||
@@ -199,7 +251,8 @@ class Install extends Action
|
||||
->setParam('httpsPort', $httpsPort)
|
||||
->setParam('version', APP_VERSION_STABLE)
|
||||
->setParam('organization', $organization)
|
||||
->setParam('image', $image);
|
||||
->setParam('image', $image)
|
||||
->setParam('enableAssistant', $enableAssistant);
|
||||
|
||||
$templateForEnv->setParam('vars', $input);
|
||||
|
||||
|
||||
@@ -75,15 +75,10 @@ class SDKs extends Action
|
||||
$git = ($git === 'yes');
|
||||
|
||||
$prUrls = [];
|
||||
$createPr = false;
|
||||
|
||||
if ($git) {
|
||||
$production ??= Console::confirm('Type "Appwrite" to push code to production git repos');
|
||||
$production = $production === 'Appwrite';
|
||||
$production = ($production === 'yes');
|
||||
$message ??= Console::confirm('Please enter your commit message:');
|
||||
|
||||
$createPr = Console::confirm('Should we create pull request automatically? (yes/no)');
|
||||
$createPr = ($createPr === 'yes');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,7 +447,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
');
|
||||
|
||||
Console::success("Pushed {$language['name']} SDK to {$gitUrl}");
|
||||
if ($createPr) {
|
||||
if ($git) {
|
||||
$prTitle = "feat: {$language['name']} SDK update for version {$language['version']}";
|
||||
$prBody = "This PR contains updates to the {$language['name']} SDK for version {$language['version']}.";
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,8 @@ class Migrations extends Action
|
||||
default => throw new \Exception('Invalid source type'),
|
||||
};
|
||||
|
||||
$this->sourceReport = $migrationSource->report();
|
||||
$resources = $migration->getAttribute('resources', []);
|
||||
$this->sourceReport = $migrationSource->report($resources);
|
||||
|
||||
return $migrationSource;
|
||||
}
|
||||
@@ -362,10 +363,10 @@ class Migrations extends Action
|
||||
$migration->getAttribute('resourceId'),
|
||||
$migration->getAttribute('resourceType')
|
||||
);
|
||||
}
|
||||
|
||||
$destination->shutdown();
|
||||
$source->shutdown();
|
||||
$destination->shutdown();
|
||||
$source->shutdown();
|
||||
}
|
||||
|
||||
$sourceErrors = $source->getErrors();
|
||||
$destinationErrors = $destination->getErrors();
|
||||
|
||||
@@ -1851,6 +1851,8 @@ class ProjectsConsoleClientTest extends Scope
|
||||
$sessionCookie = $response['headers']['set-cookie'];
|
||||
$sessionId2 = $response['body']['$id'];
|
||||
|
||||
sleep(5); // fixes flaky tests.
|
||||
|
||||
/**
|
||||
* List sessions
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user