mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'main' of https://github.com/appwrite/appwrite into sync-with-main
This commit is contained in:
@@ -263,7 +263,7 @@ class Phpass extends Hash
|
||||
*/
|
||||
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
$output = '$2a$';
|
||||
$output .= chr(ord('0') + $options['iteration_count_log2'] / 10);
|
||||
$output .= chr(ord('0') + intval($options['iteration_count_log2'] / 10));
|
||||
$output .= chr(ord('0') + $options['iteration_count_log2'] % 10);
|
||||
$output .= '$';
|
||||
$i = 0;
|
||||
|
||||
@@ -160,15 +160,6 @@ class Apple extends OAuth2
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
if (
|
||||
isset($this->claims['email']) &&
|
||||
!empty($this->claims['email']) &&
|
||||
isset($this->claims['email_verified']) &&
|
||||
$this->claims['email_verified'] === 'true'
|
||||
) {
|
||||
return $this->claims['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,6 @@ class Compose
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
return (isset($this->compose['version'])) ? $this->compose['version'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Service[]
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Queue\Client;
|
||||
use Utopia\Queue\Connection;
|
||||
|
||||
@@ -107,18 +108,30 @@ class Database extends Event
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
$this->setQueue($this->getProject()->getAttribute('database'));
|
||||
try {
|
||||
$dsn = new DSN($this->getProject()->getAttribute('database'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $this->getProject()->getAttribute('database'));
|
||||
}
|
||||
|
||||
$this->setQueue($dsn->getHost());
|
||||
|
||||
$client = new Client($this->queue, $this->connection);
|
||||
|
||||
return $client->enqueue([
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'type' => $this->type,
|
||||
'collection' => $this->collection,
|
||||
'document' => $this->document,
|
||||
'database' => $this->database,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
try {
|
||||
$result = $client->enqueue([
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'type' => $this->type,
|
||||
'collection' => $this->collection,
|
||||
'document' => $this->document,
|
||||
'database' => $this->database,
|
||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
||||
]);
|
||||
return $result;
|
||||
} catch (\Throwable $th) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class Event
|
||||
protected string $class = '';
|
||||
protected string $event = '';
|
||||
protected array $params = [];
|
||||
protected array $sensitive = [];
|
||||
protected array $payload = [];
|
||||
protected array $context = [];
|
||||
protected ?Document $project = null;
|
||||
@@ -158,12 +159,17 @@ class Event
|
||||
* Set payload for this event.
|
||||
*
|
||||
* @param array $payload
|
||||
* @param array $sensitive
|
||||
* @return self
|
||||
*/
|
||||
public function setPayload(array $payload): self
|
||||
public function setPayload(array $payload, array $sensitive = []): self
|
||||
{
|
||||
$this->payload = $payload;
|
||||
|
||||
foreach ($sensitive as $key) {
|
||||
$this->sensitive[$key] = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -177,6 +183,19 @@ class Event
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
public function getRealtimePayload(): array
|
||||
{
|
||||
$payload = [];
|
||||
|
||||
foreach ($this->payload as $key => $value) {
|
||||
if (!isset($this->sensitive[$key])) {
|
||||
$payload[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set context for this event.
|
||||
*
|
||||
@@ -239,6 +258,13 @@ class Event
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setParamSensitive(string $key): self
|
||||
{
|
||||
$this->sensitive[$key] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get param of event.
|
||||
*
|
||||
@@ -291,6 +317,7 @@ class Event
|
||||
public function reset(): self
|
||||
{
|
||||
$this->params = [];
|
||||
$this->sensitive = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ class Usage extends Event
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
$client = new Client($this->queue, $this->connection);
|
||||
|
||||
return $client->enqueue([
|
||||
'project' => $this->getProject(),
|
||||
'reduce' => $this->reduce,
|
||||
|
||||
@@ -34,7 +34,7 @@ class Event extends Validator
|
||||
public function isValid($value): bool
|
||||
{
|
||||
$events = Config::getParam('events', []);
|
||||
$parts = \explode('.', $value);
|
||||
$parts = \explode('.', $value ?? '');
|
||||
$count = \count($parts);
|
||||
|
||||
if ($count < 2 || $count > 7) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class FunctionEvent extends Event
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (str_starts_with($value, 'functions.')) {
|
||||
if (str_starts_with($value ?? false, 'functions.')) {
|
||||
$this->message = 'Triggering a function on a function event is not allowed.';
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ class Exception extends \Exception
|
||||
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing';
|
||||
public const FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout';
|
||||
|
||||
/** Deployments */
|
||||
public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
|
||||
@@ -198,6 +199,9 @@ class Exception extends \Exception
|
||||
public const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid';
|
||||
public const ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid';
|
||||
|
||||
/** Relationship */
|
||||
public const RELATIONSHIP_VALUE_INVALID = 'relationship_value_invalid';
|
||||
|
||||
/** Indexes */
|
||||
public const INDEX_NOT_FOUND = 'index_not_found';
|
||||
public const INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded';
|
||||
@@ -300,11 +304,21 @@ class Exception extends \Exception
|
||||
protected array $errors = [];
|
||||
protected bool $publish;
|
||||
|
||||
public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int $code = null, \Throwable $previous = null)
|
||||
public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int|string $code = null, \Throwable $previous = null)
|
||||
{
|
||||
$this->errors = Config::getParam('errors');
|
||||
$this->type = $type;
|
||||
$this->code = $code ?? $this->errors[$type]['code'];
|
||||
|
||||
// Mark string errors like HY001 from PDO as 500 errors
|
||||
if(\is_string($this->code)) {
|
||||
if (\is_numeric($this->code)) {
|
||||
$this->code = (int) $this->code;
|
||||
} else {
|
||||
$this->code = 500;
|
||||
}
|
||||
}
|
||||
|
||||
$this->message = $message ?? $this->errors[$type]['description'];
|
||||
|
||||
$this->publish = $this->errors[$type]['publish'] ?? ($this->code >= 500);
|
||||
|
||||
@@ -16,11 +16,14 @@ class Hooks
|
||||
|
||||
/**
|
||||
* @param mixed[] $params
|
||||
* @return mixed
|
||||
*/
|
||||
public function trigger(string $name, array $params = [])
|
||||
public function trigger(string $name, array $params = []): mixed
|
||||
{
|
||||
if (isset(self::$hooks[$name])) {
|
||||
call_user_func_array(self::$hooks[$name], $params);
|
||||
return call_user_func_array(self::$hooks[$name], $params);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ abstract class Migration
|
||||
'1.5.3' => 'V20',
|
||||
'1.5.4' => 'V20',
|
||||
'1.5.5' => 'V20',
|
||||
'1.5.6' => 'V20',
|
||||
'1.5.7' => 'V20',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
namespace Appwrite\Platform;
|
||||
|
||||
use Appwrite\Platform\Services\Tasks;
|
||||
use Appwrite\Platform\Services\Workers;
|
||||
use Appwrite\Platform\Modules\Core;
|
||||
use Utopia\Platform\Platform;
|
||||
|
||||
class Appwrite extends Platform
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('tasks', new Tasks());
|
||||
$this->addService('workers', new Workers());
|
||||
parent::__construct(new Core());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules;
|
||||
|
||||
use Appwrite\Platform\Services\Tasks;
|
||||
use Appwrite\Platform\Services\Workers;
|
||||
use Utopia\Platform\Module;
|
||||
|
||||
class Core extends Module
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('tasks', new Tasks());
|
||||
$this->addService('workers', new Workers());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class Tasks extends Service
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = self::TYPE_CLI;
|
||||
$this->type = Service::TYPE_TASK;
|
||||
$this
|
||||
->addAction(Doctor::getName(), new Doctor())
|
||||
->addAction(Install::getName(), new Install())
|
||||
|
||||
@@ -20,7 +20,7 @@ class Workers extends Service
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = self::TYPE_WORKER;
|
||||
$this->type = Service::TYPE_WORKER;
|
||||
$this
|
||||
->addAction(Audits::getName(), new Audits())
|
||||
->addAction(Builds::getName(), new Builds())
|
||||
|
||||
@@ -49,6 +49,7 @@ class Maintenance extends Action
|
||||
$this->foreachProject($dbForConsole, function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) {
|
||||
$queueForDeletes->setProject($project);
|
||||
|
||||
$this->notifyDeleteTargets($queueForDeletes);
|
||||
$this->notifyDeleteExecutionLogs($queueForDeletes);
|
||||
$this->notifyDeleteAbuseLogs($queueForDeletes);
|
||||
$this->notifyDeleteAuditLogs($queueForDeletes);
|
||||
@@ -60,7 +61,6 @@ class Maintenance extends Action
|
||||
$this->renewCertificates($dbForConsole, $queueForCertificates);
|
||||
$this->notifyDeleteCache($cacheRetention, $queueForDeletes);
|
||||
$this->notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes);
|
||||
$this->notifyDeleteTargets($queueForDeletes);
|
||||
}, $interval, $delay);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Client;
|
||||
use Utopia\Queue\Connection;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class QueueCount extends Action
|
||||
@@ -21,19 +21,7 @@ class QueueCount extends Action
|
||||
{
|
||||
$this
|
||||
->desc('Return the number of from a specific queue identified by the name parameter with a specific type')
|
||||
->param('name', '', new WhiteList([
|
||||
Event::DATABASE_QUEUE_NAME,
|
||||
Event::DELETE_QUEUE_NAME,
|
||||
Event::AUDITS_QUEUE_NAME,
|
||||
Event::MAILS_QUEUE_NAME,
|
||||
Event::FUNCTIONS_QUEUE_NAME,
|
||||
Event::USAGE_QUEUE_NAME,
|
||||
Event::WEBHOOK_QUEUE_NAME,
|
||||
Event::CERTIFICATES_QUEUE_NAME,
|
||||
Event::BUILDS_QUEUE_NAME,
|
||||
Event::MESSAGING_QUEUE_NAME,
|
||||
Event::MIGRATIONS_QUEUE_NAME
|
||||
]), 'Queue name')
|
||||
->param('name', '', new Text(100), 'Queue name')
|
||||
->param('type', '', new WhiteList([
|
||||
'success',
|
||||
'failed',
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Client;
|
||||
use Utopia\Queue\Connection;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Wildcard;
|
||||
|
||||
class QueueRetry extends Action
|
||||
@@ -22,19 +21,7 @@ class QueueRetry extends Action
|
||||
{
|
||||
$this
|
||||
->desc('Retry failed jobs from a specific queue identified by the name parameter')
|
||||
->param('name', '', new WhiteList([
|
||||
Event::DATABASE_QUEUE_NAME,
|
||||
Event::DELETE_QUEUE_NAME,
|
||||
Event::AUDITS_QUEUE_NAME,
|
||||
Event::MAILS_QUEUE_NAME,
|
||||
Event::FUNCTIONS_QUEUE_NAME,
|
||||
Event::USAGE_QUEUE_NAME,
|
||||
Event::WEBHOOK_CLASS_NAME,
|
||||
Event::CERTIFICATES_QUEUE_NAME,
|
||||
Event::BUILDS_QUEUE_NAME,
|
||||
Event::MESSAGING_QUEUE_NAME,
|
||||
Event::MIGRATIONS_QUEUE_NAME
|
||||
]), 'Queue name')
|
||||
->param('name', '', new Text(100), 'Queue name')
|
||||
->param('limit', 0, new Wildcard(), 'jobs limit', true)
|
||||
->inject('queue')
|
||||
->callback(fn ($name, $limit, $queue) => $this->action($name, $limit, $queue));
|
||||
|
||||
@@ -15,6 +15,7 @@ use Appwrite\SDK\Language\Kotlin;
|
||||
use Appwrite\SDK\Language\Node;
|
||||
use Appwrite\SDK\Language\PHP;
|
||||
use Appwrite\SDK\Language\Python;
|
||||
use Appwrite\SDK\Language\ReactNative;
|
||||
use Appwrite\SDK\Language\REST;
|
||||
use Appwrite\SDK\Language\Ruby;
|
||||
use Appwrite\SDK\Language\Swift;
|
||||
@@ -159,6 +160,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
$config = new Flutter();
|
||||
$config->setPackageName('appwrite');
|
||||
break;
|
||||
case 'react-native':
|
||||
$config = new ReactNative();
|
||||
$config->setNPMPackage('react-native-appwrite');
|
||||
break;
|
||||
case 'flutter-dev':
|
||||
$config = new Flutter();
|
||||
$config->setPackageName('appwrite_dev');
|
||||
|
||||
@@ -262,7 +262,7 @@ class Builds extends Action
|
||||
Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
|
||||
// Merge template into user repo
|
||||
Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
Console::execute('rsync -av --exclude \'.git\' ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/ ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
|
||||
// Commit and push
|
||||
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $stdout, $stderr);
|
||||
@@ -447,6 +447,8 @@ class Builds extends Action
|
||||
throw new \Exception('Build not found', 404);
|
||||
}
|
||||
|
||||
$logs = \mb_substr($logs, 0, null, 'UTF-8'); // Get only valid UTF8 part - removes leftover half-multibytes causing SQL errors
|
||||
|
||||
$build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs);
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build);
|
||||
|
||||
|
||||
@@ -135,9 +135,9 @@ class Certificates extends Action
|
||||
|
||||
try {
|
||||
// Email for alerts is required by LetsEncrypt
|
||||
$email = System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS');
|
||||
$email = System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'));
|
||||
if (empty($email)) {
|
||||
throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.');
|
||||
throw new Exception('You must set a valid security email address (_APP_EMAIL_CERTIFICATES) to issue an SSL certificate.');
|
||||
}
|
||||
|
||||
// Validate domain and DNS records. Skip if job is forced
|
||||
@@ -474,7 +474,7 @@ class Certificates extends Action
|
||||
->setBody($body)
|
||||
->setName('Appwrite Administrator')
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient(System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'))
|
||||
->setRecipient(System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')))
|
||||
->trigger();
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,11 @@ class Databases extends Action
|
||||
*/
|
||||
$attribute = $dbForProject->getDocument('attributes', $attribute->getId());
|
||||
|
||||
if ($attribute->isEmpty()) {
|
||||
// Attribute was deleted before job was processed
|
||||
return;
|
||||
}
|
||||
|
||||
$collectionId = $collection->getId();
|
||||
$key = $attribute->getAttribute('key', '');
|
||||
$type = $attribute->getAttribute('type', '');
|
||||
@@ -176,7 +181,6 @@ class Databases extends Action
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$dbForProject->updateDocument(
|
||||
'attributes',
|
||||
$attribute->getId(),
|
||||
|
||||
@@ -12,6 +12,7 @@ use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
@@ -21,6 +22,7 @@ use Utopia\Database\Exception\Conflict;
|
||||
use Utopia\Database\Exception\Restricted;
|
||||
use Utopia\Database\Exception\Structure;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
@@ -441,7 +443,7 @@ class Deletes extends Action
|
||||
* @param Document $document
|
||||
* @return void
|
||||
* @throws Authorization
|
||||
* @throws \Utopia\Database\Exception
|
||||
* @throws DatabaseException
|
||||
* @throws Conflict
|
||||
* @throws Restricted
|
||||
* @throws Structure
|
||||
@@ -469,25 +471,48 @@ class Deletes extends Action
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws Authorization
|
||||
* @throws \Utopia\Database\Exception
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
private function deleteProject(Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, Document $document): void
|
||||
{
|
||||
$projectId = $document->getId();
|
||||
$projectInternalId = $document->getInternalId();
|
||||
|
||||
// Delete project tables
|
||||
try {
|
||||
$dsn = new DSN($document->getAttribute('database', 'console'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $document->getAttribute('database', 'console'));
|
||||
}
|
||||
|
||||
$dbForProject = $getProjectDB($document);
|
||||
|
||||
while (true) {
|
||||
$collections = $dbForProject->listCollections();
|
||||
$projectCollectionIds = [
|
||||
...\array_keys(Config::getParam('collections', [])['projects']),
|
||||
Audit::COLLECTION,
|
||||
TimeLimit::COLLECTION,
|
||||
];
|
||||
|
||||
if (empty($collections)) {
|
||||
break;
|
||||
}
|
||||
$limit = \count($projectCollectionIds) + 25;
|
||||
|
||||
while (true) {
|
||||
$collections = $dbForProject->listCollections($limit);
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
$dbForProject->deleteCollection($collection->getId());
|
||||
if ($dsn->getHost() !== DATABASE_SHARED_TABLES || !\in_array($collection->getId(), $projectCollectionIds)) {
|
||||
$dbForProject->deleteCollection($collection->getId());
|
||||
} else {
|
||||
$this->deleteByGroup($collection->getId(), [], database: $dbForProject);
|
||||
}
|
||||
}
|
||||
|
||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
||||
$collectionsIds = \array_map(fn ($collection) => $collection->getId(), $collections);
|
||||
|
||||
if (empty(\array_diff($collectionsIds, $projectCollectionIds))) {
|
||||
break;
|
||||
}
|
||||
} elseif (empty($collections)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,17 +548,16 @@ class Deletes extends Action
|
||||
Query::equal('projectInternalId', [$projectInternalId]),
|
||||
], $dbForConsole);
|
||||
|
||||
// Delete VCS commments
|
||||
// Delete VCS comments
|
||||
$this->deleteByGroup('vcsComments', [
|
||||
Query::equal('projectInternalId', [$projectInternalId]),
|
||||
], $dbForConsole);
|
||||
|
||||
// Delete metadata tables
|
||||
try {
|
||||
// Delete metadata table
|
||||
if ($dsn->getHost() !== DATABASE_SHARED_TABLES) {
|
||||
$dbForProject->deleteCollection('_metadata');
|
||||
} catch (\Throwable) {
|
||||
// Ignore: deleteCollection tries to delete a metadata entry after the collection is deleted,
|
||||
// which will throw an exception here because the metadata collection is already deleted.
|
||||
} else {
|
||||
$this->deleteByGroup('_metadata', [], $dbForProject);
|
||||
}
|
||||
|
||||
// Delete all storage directories
|
||||
@@ -660,9 +684,11 @@ class Deletes extends Action
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$timeLimit = new TimeLimit("", 0, 1, $dbForProject);
|
||||
$abuse = new Abuse($timeLimit);
|
||||
$status = $abuse->cleanup($abuseRetention);
|
||||
if (!$status) {
|
||||
throw new Exception('Failed to delete Abuse logs for project ' . $projectId);
|
||||
|
||||
try {
|
||||
$abuse->cleanup($abuseRetention);
|
||||
} catch (DatabaseException $e) {
|
||||
Console::error('Failed to delete abuse logs for project ' . $projectId . ': ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,9 +704,11 @@ class Deletes extends Action
|
||||
$projectId = $project->getId();
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$audit = new Audit($dbForProject);
|
||||
$status = $audit->cleanup($auditRetention);
|
||||
if (!$status) {
|
||||
throw new Exception('Failed to delete Audit logs for project' . $projectId);
|
||||
|
||||
try {
|
||||
$audit->cleanup($auditRetention);
|
||||
} catch (DatabaseException $e) {
|
||||
Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,7 +963,12 @@ class Deletes extends Action
|
||||
while ($sum === $limit) {
|
||||
$chunk++;
|
||||
|
||||
$results = $database->find($collection, \array_merge([Query::limit($limit)], $queries));
|
||||
try {
|
||||
$results = $database->find($collection, [Query::limit($limit), ...$queries]);
|
||||
} catch (DatabaseException $e) {
|
||||
Console::error('Failed to find documents for collection ' . $collection . ': ' . $e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
|
||||
@@ -199,6 +199,70 @@ class Functions extends Action
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param Document $function
|
||||
* @param string $trigger
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param Document $user
|
||||
* @param string|null $jwt
|
||||
* @param string|null $event
|
||||
* @throws Exception
|
||||
*/
|
||||
private function fail(
|
||||
string $message,
|
||||
Database $dbForProject,
|
||||
Document $function,
|
||||
string $trigger,
|
||||
string $path,
|
||||
string $method,
|
||||
Document $user,
|
||||
string $jwt = null,
|
||||
string $event = null,
|
||||
): void {
|
||||
$headers['x-appwrite-trigger'] = $trigger;
|
||||
$headers['x-appwrite-event'] = $event ?? '';
|
||||
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
|
||||
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
|
||||
|
||||
$headersFiltered = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) {
|
||||
$headersFiltered[] = ['name' => $key, 'value' => $value];
|
||||
}
|
||||
}
|
||||
|
||||
$executionId = ID::unique();
|
||||
$execution = new Document([
|
||||
'$id' => $executionId,
|
||||
'$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))],
|
||||
'functionInternalId' => $function->getInternalId(),
|
||||
'functionId' => $function->getId(),
|
||||
'deploymentInternalId' => '',
|
||||
'deploymentId' => '',
|
||||
'trigger' => $trigger,
|
||||
'status' => 'failed',
|
||||
'responseStatusCode' => 0,
|
||||
'responseHeaders' => [],
|
||||
'requestPath' => $path,
|
||||
'requestMethod' => $method,
|
||||
'requestHeaders' => $headersFiltered,
|
||||
'errors' => $message,
|
||||
'logs' => '',
|
||||
'duration' => 0.0,
|
||||
'search' => implode(' ', [$function->getId(), $executionId]),
|
||||
]);
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
$execution = $dbForProject->createDocument('executions', $execution);
|
||||
}
|
||||
|
||||
if ($execution->isEmpty()) {
|
||||
throw new Exception('Failed to create execution');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Log $log
|
||||
* @param Database $dbForProject
|
||||
@@ -252,11 +316,15 @@ class Functions extends Action
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
if ($deployment->getAttribute('resourceId') !== $functionId) {
|
||||
throw new Exception('Deployment not found. Create deployment before trying to execute a function');
|
||||
$errorMessage = 'The execution could not be completed because a corresponding deployment was not found. A function deployment needs to be created before it can be executed. Please create a deployment for your function and try again.';
|
||||
$this->fail($errorMessage, $dbForProject, $function, $trigger, $path, $method, $user, $jwt, $event);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($deployment->isEmpty()) {
|
||||
throw new Exception('Deployment not found. Create deployment before trying to execute a function');
|
||||
$errorMessage = 'The execution could not be completed because a corresponding deployment was not found. A function deployment needs to be created before it can be executed. Please create a deployment for your function and try again.';
|
||||
$this->fail($errorMessage, $dbForProject, $function, $trigger, $path, $method, $user, $jwt, $event);
|
||||
return;
|
||||
}
|
||||
|
||||
$buildId = $deployment->getAttribute('buildId', '');
|
||||
@@ -266,11 +334,15 @@ class Functions extends Action
|
||||
/** Check if build has exists */
|
||||
$build = $dbForProject->getDocument('builds', $buildId);
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found');
|
||||
$errorMessage = 'The execution could not be completed because a corresponding deployment was not found. A function deployment needs to be created before it can be executed. Please create a deployment for your function and try again.';
|
||||
$this->fail($errorMessage, $dbForProject, $function, $trigger, $path, $method, $user, $jwt, $event);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') !== 'ready') {
|
||||
throw new Exception('Build not ready');
|
||||
$errorMessage = 'The execution could not be completed because the build is not ready. Please wait for the build to complete and try again.';
|
||||
$this->fail($errorMessage, $dbForProject, $function, $trigger, $path, $method, $user, $jwt, $event);
|
||||
return;
|
||||
}
|
||||
|
||||
/** Check if runtime is supported */
|
||||
@@ -296,7 +368,6 @@ class Functions extends Action
|
||||
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
|
||||
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
|
||||
|
||||
/** Create execution or update execution status */
|
||||
/** Create execution or update execution status */
|
||||
$execution = $dbForProject->getDocument('executions', $executionId ?? '');
|
||||
if ($execution->isEmpty()) {
|
||||
|
||||
@@ -25,7 +25,7 @@ use Utopia\Messaging\Adapter\SMS as SMSAdapter;
|
||||
use Utopia\Messaging\Adapter\SMS\Mock;
|
||||
use Utopia\Messaging\Adapter\SMS\Msg91;
|
||||
use Utopia\Messaging\Adapter\SMS\Telesign;
|
||||
use Utopia\Messaging\Adapter\SMS\Textmagic;
|
||||
use Utopia\Messaging\Adapter\SMS\TextMagic;
|
||||
use Utopia\Messaging\Adapter\SMS\Twilio;
|
||||
use Utopia\Messaging\Adapter\SMS\Vonage;
|
||||
use Utopia\Messaging\Messages\Email;
|
||||
@@ -35,6 +35,7 @@ use Utopia\Messaging\Messages\SMS;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -42,6 +43,8 @@ use function Swoole\Coroutine\batch;
|
||||
|
||||
class Messaging extends Action
|
||||
{
|
||||
private ?Local $localDevice = null;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'messaging';
|
||||
@@ -58,9 +61,8 @@ class Messaging extends Action
|
||||
->inject('log')
|
||||
->inject('dbForProject')
|
||||
->inject('deviceForFiles')
|
||||
->inject('deviceForLocalFiles')
|
||||
->inject('queueForUsage')
|
||||
->callback(fn (Message $message, Log $log, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocalFiles, Usage $queueForUsage) => $this->action($message, $log, $dbForProject, $deviceForFiles, $deviceForLocalFiles, $queueForUsage));
|
||||
->callback(fn (Message $message, Log $log, Database $dbForProject, Device $deviceForFiles, Usage $queueForUsage) => $this->action($message, $log, $dbForProject, $deviceForFiles, $queueForUsage));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +70,6 @@ class Messaging extends Action
|
||||
* @param Log $log
|
||||
* @param Database $dbForProject
|
||||
* @param Device $deviceForFiles
|
||||
* @param Device $deviceForLocalFiles
|
||||
* @param Usage $queueForUsage
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
@@ -78,7 +79,6 @@ class Messaging extends Action
|
||||
Log $log,
|
||||
Database $dbForProject,
|
||||
Device $deviceForFiles,
|
||||
Device $deviceForLocalFiles,
|
||||
Usage $queueForUsage
|
||||
): void {
|
||||
Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP);
|
||||
@@ -101,7 +101,7 @@ class Messaging extends Action
|
||||
case MESSAGE_SEND_TYPE_EXTERNAL:
|
||||
$message = $dbForProject->getDocument('messages', $payload['messageId']);
|
||||
|
||||
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $deviceForLocalFiles, );
|
||||
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Unknown message type: ' . $type);
|
||||
@@ -112,7 +112,7 @@ class Messaging extends Action
|
||||
Database $dbForProject,
|
||||
Document $message,
|
||||
Device $deviceForFiles,
|
||||
Device $deviceForLocalFiles,
|
||||
Document $project,
|
||||
): void {
|
||||
$topicIds = $message->getAttribute('topics', []);
|
||||
$targetIds = $message->getAttribute('targets', []);
|
||||
@@ -218,8 +218,8 @@ class Messaging extends Action
|
||||
/**
|
||||
* @var array<array> $results
|
||||
*/
|
||||
$results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||
return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||
$results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project) {
|
||||
return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project) {
|
||||
if (\array_key_exists($providerId, $providers)) {
|
||||
$provider = $providers[$providerId];
|
||||
} else {
|
||||
@@ -246,8 +246,8 @@ class Messaging extends Action
|
||||
$adapter->getMaxMessagesPerRequest()
|
||||
);
|
||||
|
||||
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||
return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $project) {
|
||||
return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $project) {
|
||||
$deliveredTotal = 0;
|
||||
$deliveryErrors = [];
|
||||
$messageData = clone $message;
|
||||
@@ -256,7 +256,7 @@ class Messaging extends Action
|
||||
$data = match ($provider->getAttribute('type')) {
|
||||
MESSAGE_TYPE_SMS => $this->buildSmsMessage($messageData, $provider),
|
||||
MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData),
|
||||
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider, $deviceForFiles, $deviceForLocalFiles),
|
||||
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider, $deviceForFiles, $project),
|
||||
default => throw new \Exception('Provider with the requested ID is of the incorrect type')
|
||||
};
|
||||
|
||||
@@ -354,8 +354,8 @@ class Messaging extends Action
|
||||
|
||||
$path = $file->getAttribute('path', '');
|
||||
|
||||
if ($deviceForLocalFiles->exists($path)) {
|
||||
$deviceForLocalFiles->delete($path);
|
||||
if ($this->getLocalDevice($project)->exists($path)) {
|
||||
$this->getLocalDevice($project)->delete($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,17 +441,24 @@ class Messaging extends Action
|
||||
try {
|
||||
$adapter->send($data);
|
||||
|
||||
$countryCode = $adapter->getCountryCode($message['to'][0] ?? '');
|
||||
if (!empty($countryCode)) {
|
||||
$queueForUsage
|
||||
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_MESSAGES_COUNTRY_CODE), 1);
|
||||
}
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_MESSAGES, 1)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
} catch (\Throwable $e) {
|
||||
throw new \Exception('Failed sending to targets with error: ' . $e->getMessage());
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Exception('Failed sending to targets with error: ' . $th->getMessage());
|
||||
}
|
||||
};
|
||||
}, $batches));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function getSmsAdapter(Document $provider): ?SMSAdapter
|
||||
{
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
@@ -459,7 +466,7 @@ class Messaging extends Action
|
||||
return match ($provider->getAttribute('provider')) {
|
||||
'mock' => new Mock('username', 'password'),
|
||||
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
|
||||
'textmagic' => new Textmagic($credentials['username'], $credentials['apiKey']),
|
||||
'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']),
|
||||
'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']),
|
||||
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']),
|
||||
'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']),
|
||||
@@ -517,7 +524,7 @@ class Messaging extends Action
|
||||
Document $message,
|
||||
Document $provider,
|
||||
Device $deviceForFiles,
|
||||
Device $deviceForLocalFiles,
|
||||
Document $project,
|
||||
): Email {
|
||||
$fromName = $provider['options']['fromName'] ?? null;
|
||||
$fromEmail = $provider['options']['fromEmail'] ?? null;
|
||||
@@ -579,7 +586,7 @@ class Messaging extends Action
|
||||
}
|
||||
|
||||
if ($deviceForFiles->getType() !== Storage::DEVICE_LOCAL) {
|
||||
$deviceForFiles->transfer($path, $path, $deviceForLocalFiles);
|
||||
$deviceForFiles->transfer($path, $path, $this->getLocalDevice($project));
|
||||
}
|
||||
|
||||
$attachment = new Attachment(
|
||||
@@ -651,4 +658,13 @@ class Messaging extends Action
|
||||
$badge
|
||||
);
|
||||
}
|
||||
|
||||
private function getLocalDevice($project): Local
|
||||
{
|
||||
if($this->localDevice === null) {
|
||||
$this->localDevice = new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
}
|
||||
|
||||
return $this->localDevice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use Utopia\Database\Exception\Restricted;
|
||||
use Utopia\Database\Exception\Structure;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Log\Breadcrumb;
|
||||
use Utopia\Migration\Destinations\Appwrite as DestinationsAppwrite;
|
||||
use Utopia\Migration\Exception as MigrationException;
|
||||
use Utopia\Migration\Source;
|
||||
@@ -85,6 +86,7 @@ class Migrations extends Action
|
||||
return;
|
||||
}
|
||||
|
||||
$log->addTag('migrationId', $migration->getId());
|
||||
$log->addTag('projectId', $project->getId());
|
||||
|
||||
$this->processMigration($project, $migration, $log);
|
||||
@@ -256,6 +258,7 @@ class Migrations extends Action
|
||||
$migrationDocument = $this->dbForProject->getDocument('migrations', $migration->getId());
|
||||
$migrationDocument->setAttribute('stage', 'processing');
|
||||
$migrationDocument->setAttribute('status', 'processing');
|
||||
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'processing'", \microtime(true)));
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
|
||||
$log->addTag('type', $migrationDocument->getAttribute('source'));
|
||||
@@ -277,6 +280,7 @@ class Migrations extends Action
|
||||
|
||||
/** Start Transfer */
|
||||
$migrationDocument->setAttribute('stage', 'migrating');
|
||||
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'migrating'", \microtime(true)));
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
$transfer->run($migrationDocument->getAttribute('resources'), function () use ($migrationDocument, $transfer, $projectDocument) {
|
||||
$migrationDocument->setAttribute('resourceData', json_encode($transfer->getCache()));
|
||||
@@ -291,6 +295,7 @@ class Migrations extends Action
|
||||
if (!empty($sourceErrors) || !empty($destinationErrors)) {
|
||||
$migrationDocument->setAttribute('status', 'failed');
|
||||
$migrationDocument->setAttribute('stage', 'finished');
|
||||
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'finished' and failed", \microtime(true)));
|
||||
|
||||
$errorMessages = [];
|
||||
foreach ($sourceErrors as $error) {
|
||||
@@ -303,6 +308,7 @@ class Migrations extends Action
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
$log->addExtra('migrationErrors', json_encode($errorMessages));
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
|
||||
return;
|
||||
@@ -310,6 +316,7 @@ class Migrations extends Action
|
||||
|
||||
$migrationDocument->setAttribute('status', 'completed');
|
||||
$migrationDocument->setAttribute('stage', 'finished');
|
||||
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'finished' and succeeded", \microtime(true)));
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
|
||||
@@ -338,6 +345,7 @@ class Migrations extends Action
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
$log->addTag('migrationErrors', json_encode($errorMessages));
|
||||
}
|
||||
} finally {
|
||||
if ($tempAPIKey) {
|
||||
@@ -347,7 +355,7 @@ class Migrations extends Action
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
|
||||
if ($migrationDocument->getAttribute('status', '') == 'failed') {
|
||||
throw new Exception(implode("\n", $migrationDocument->getAttribute('errors', [])));
|
||||
throw new Exception("Migration failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class Webhooks extends Action
|
||||
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(
|
||||
APP_USERAGENT,
|
||||
System::getEnv('_APP_VERSION', 'UNKNOWN'),
|
||||
System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
|
||||
System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY))
|
||||
));
|
||||
\curl_setopt(
|
||||
$ch,
|
||||
|
||||
@@ -96,15 +96,15 @@ class OpenAPI3 extends Format
|
||||
];
|
||||
|
||||
if (isset($output['components']['securitySchemes']['Project'])) {
|
||||
$output['components']['securitySchemes']['Project']['x-appwrite'] = ['demo' => '5df5acd0d48c2'];
|
||||
$output['components']['securitySchemes']['Project']['x-appwrite'] = ['demo' => '<YOUR_PROJECT_ID>'];
|
||||
}
|
||||
|
||||
if (isset($output['components']['securitySchemes']['Key'])) {
|
||||
$output['components']['securitySchemes']['Key']['x-appwrite'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
|
||||
$output['components']['securitySchemes']['Key']['x-appwrite'] = ['demo' => '<YOUR_API_KEY>'];
|
||||
}
|
||||
|
||||
if (isset($output['securityDefinitions']['JWT'])) {
|
||||
$output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...'];
|
||||
$output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => '<YOUR_JWT>'];
|
||||
}
|
||||
|
||||
if (isset($output['components']['securitySchemes']['Locale'])) {
|
||||
|
||||
@@ -93,15 +93,15 @@ class Swagger2 extends Format
|
||||
];
|
||||
|
||||
if (isset($output['securityDefinitions']['Project'])) {
|
||||
$output['securityDefinitions']['Project']['x-appwrite'] = ['demo' => '5df5acd0d48c2'];
|
||||
$output['securityDefinitions']['Project']['x-appwrite'] = ['demo' => '<YOUR_PROJECT_ID>'];
|
||||
}
|
||||
|
||||
if (isset($output['securityDefinitions']['Key'])) {
|
||||
$output['securityDefinitions']['Key']['x-appwrite'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
|
||||
$output['securityDefinitions']['Key']['x-appwrite'] = ['demo' => '<YOUR_API_KEY>'];
|
||||
}
|
||||
|
||||
if (isset($output['securityDefinitions']['JWT'])) {
|
||||
$output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...'];
|
||||
$output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => '<YOUR_JWT>'];
|
||||
}
|
||||
|
||||
if (isset($output['securityDefinitions']['Locale'])) {
|
||||
|
||||
@@ -67,9 +67,6 @@ class Comment
|
||||
];
|
||||
}
|
||||
|
||||
//TODO: Update link to documentation
|
||||
$text .= "**Your function has been automatically deployed.** Learn more about Appwrite [Function Deployments](https://appwrite.io/docs/functions).\n\n";
|
||||
|
||||
foreach ($projects as $projectId => $project) {
|
||||
$text .= "**{$project['name']}** `{$projectId}`\n\n";
|
||||
$text .= "| Function | ID | Status | Action |\n";
|
||||
@@ -79,6 +76,18 @@ class Comment
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
foreach ($project['functions'] as $functionId => $function) {
|
||||
if ($function['status'] === 'waiting' || $function['status'] === 'processing' || $function['status'] === 'building') {
|
||||
$text .= "**Your function deployment is in progress. Please check back in a few minutes for the updated status.**\n\n";
|
||||
} elseif ($function['status'] === 'ready') {
|
||||
$text .= "**Your function has been successfully deployed.**\n\n";
|
||||
} else {
|
||||
$text .= "**Your function deployment has failed. Please check the logs for more details and retry.**\n\n";
|
||||
}
|
||||
|
||||
$text .= "Project name: **{$project['name']}** \nProject ID: `{$projectId}`\n\n";
|
||||
$text .= "| Function | ID | Status | Action |\n";
|
||||
$text .= "| :- | :- | :- | :- |\n";
|
||||
|
||||
$generateImage = function (string $status) use ($protocol, $hostname) {
|
||||
$extention = $status === 'building' ? 'gif' : 'png';
|
||||
$imagesUrl = $protocol . '://' . $hostname . '/images/vcs/';
|
||||
@@ -106,7 +115,9 @@ class Comment
|
||||
|
||||
$text .= "\n\n";
|
||||
}
|
||||
//TODO: Update did you know section
|
||||
$functionUrl = $protocol . '://' . $hostname . '/console/project-' . $projectId . '/functions/function-' . $functionId;
|
||||
$text .= "Only deployments on the production branch are activated automatically. If you'd like to activate this deployment, navigate to [your deployments]($functionUrl). Learn more about Appwrite [Function deployments](https://appwrite.io/docs/functions).\n\n";
|
||||
|
||||
$tip = $this->tips[array_rand($this->tips)];
|
||||
$text .= "> **💡 Did you know?** \n " . $tip . "\n\n";
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Executor;
|
||||
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Exception;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -193,7 +194,6 @@ class Executor
|
||||
'path' => $path,
|
||||
'method' => $method,
|
||||
'headers' => $headers,
|
||||
|
||||
'image' => $image,
|
||||
'source' => $source,
|
||||
'entrypoint' => $entrypoint,
|
||||
@@ -311,6 +311,8 @@ class Executor
|
||||
|
||||
$responseType = $responseHeaders['content-type'] ?? '';
|
||||
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_errno($ch);
|
||||
$curlErrorMessage = curl_error($ch);
|
||||
|
||||
if ($decode) {
|
||||
switch (substr($responseType, 0, strpos($responseType, ';'))) {
|
||||
@@ -327,8 +329,11 @@ class Executor
|
||||
}
|
||||
}
|
||||
|
||||
if ((curl_errno($ch)/* || 200 != $responseStatus*/)) {
|
||||
throw new Exception(curl_error($ch) . ' with status code ' . $responseStatus, $responseStatus);
|
||||
if ($curlError) {
|
||||
if ($curlError == CURLE_OPERATION_TIMEDOUT) {
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_SYNCHRONOUS_TIMEOUT);
|
||||
}
|
||||
throw new Exception($curlErrorMessage . ' with status code ' . $responseStatus, $responseStatus);
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
Reference in New Issue
Block a user