mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge remote-tracking branch 'origin/1.8.x' into documents-db-api
This commit is contained in:
@@ -51,14 +51,14 @@ class Origin extends Validator
|
||||
*/
|
||||
public function isValid($origin): bool
|
||||
{
|
||||
$this->origin = $origin;
|
||||
$this->scheme = null;
|
||||
$this->host = null;
|
||||
|
||||
if (!is_string($origin) || empty($origin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->origin = $origin;
|
||||
$this->scheme = null;
|
||||
$this->host = null;
|
||||
|
||||
$this->scheme = $this->parseScheme($origin);
|
||||
$this->host = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ class Create extends Base
|
||||
|
||||
if ($async) {
|
||||
if (is_null($scheduledAt)) {
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
if ($project->getId() != '6862e6a6000cce69f9da') {
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
$queueForFunctions
|
||||
@@ -344,7 +344,7 @@ class Create extends Base
|
||||
->setAttribute('scheduleInternalId', $schedule->getSequence())
|
||||
->setAttribute('scheduledAt', $scheduledAt);
|
||||
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
if ($project->getId() != '6862e6a6000cce69f9da') {
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
}
|
||||
@@ -505,7 +505,7 @@ class Create extends Base
|
||||
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
|
||||
;
|
||||
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
if ($project->getId() != '6862e6a6000cce69f9da') {
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,15 +111,16 @@ class Screenshots extends Action
|
||||
throw new \Exception('Bucket not found');
|
||||
}
|
||||
|
||||
$routerHost = System::getEnv('_APP_WORKER_SCREENSHOTS_ROUTER', 'http://appwrite');
|
||||
$configs = [
|
||||
'screenshotLight' => [
|
||||
'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ],
|
||||
'url' => 'http://appwrite/?appwrite-preview=1&appwrite-theme=light',
|
||||
'url' => $routerHost . '/?appwrite-preview=1&appwrite-theme=light',
|
||||
'theme' => 'light'
|
||||
],
|
||||
'screenshotDark' => [
|
||||
'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ],
|
||||
'url' => 'http://appwrite/?appwrite-preview=1&appwrite-theme=dark',
|
||||
'url' => $routerHost . '/?appwrite-preview=1&appwrite-theme=dark',
|
||||
'theme' => 'dark'
|
||||
],
|
||||
];
|
||||
|
||||
@@ -263,6 +263,7 @@ class Create extends Action
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
'database' => $dsn,
|
||||
'labels' => [],
|
||||
'status' => PROJECT_STATUS_ACTIVE,
|
||||
'documentsDatabase' => $this->getDatabaseDSN('documentsDatabase', $region, $dsn),
|
||||
'vectorDatabase' => $this->getDatabaseDSN('vectorDatabase', $region, $dsn)
|
||||
]));
|
||||
|
||||
@@ -9,7 +9,6 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\ListSelection;
|
||||
use Utopia\Config\Config;
|
||||
@@ -68,13 +67,13 @@ class XList extends Action
|
||||
->param('queries', [], $this->getQueriesValidator(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Projects::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('team')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(array $queries, string $search, bool $includeTotal, Request $request, Response $response, Database $dbForPlatform)
|
||||
public function action(array $queries, string $search, bool $includeTotal, Response $response, Database $dbForPlatform, Document $team)
|
||||
{
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
@@ -86,6 +85,10 @@ class XList extends Action
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
if (!$team->isEmpty()) {
|
||||
$queries[] = Query::equal('teamInternalId', [$team->getSequence()]);
|
||||
}
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\SDK\Language\AgentSkills;
|
||||
use Appwrite\SDK\Language\Android;
|
||||
use Appwrite\SDK\Language\Apple;
|
||||
use Appwrite\SDK\Language\CLI;
|
||||
@@ -51,6 +52,7 @@ class SDKs extends Action
|
||||
'graphql',
|
||||
'rest',
|
||||
'markdown',
|
||||
'agent-skills'
|
||||
];
|
||||
|
||||
public static function getName(): string
|
||||
@@ -284,6 +286,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
$config = new Markdown();
|
||||
$config->setNPMPackage('@appwrite.io/docs');
|
||||
break;
|
||||
case 'agent-skills':
|
||||
$config = new AgentSkills();
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Language "' . $language['key'] . '" not supported');
|
||||
}
|
||||
@@ -576,9 +581,16 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
foreach ($docDirectories as $languageTitle => $path) {
|
||||
$languagePath = strtolower($languageTitle !== 0 ? '/' . $languageTitle : '');
|
||||
$examplesSource = $result . '/docs/examples' . $languagePath;
|
||||
|
||||
if (!\is_dir($examplesSource)) {
|
||||
Console::warning("No code examples found for {$language['name']} SDK at: {$examplesSource}. Skipping copy.");
|
||||
continue;
|
||||
}
|
||||
|
||||
\exec(
|
||||
'mkdir -p ' . $resultExamples . $languagePath . ' && \
|
||||
cp -r ' . $result . '/docs/examples' . $languagePath . ' ' . $resultExamples
|
||||
cp -r ' . $examplesSource . ' ' . $resultExamples
|
||||
);
|
||||
Console::success("Copied code examples for {$language['name']} SDK to: {$resultExamples}");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Executions extends Action
|
||||
{
|
||||
@@ -45,7 +44,8 @@ class Executions extends Action
|
||||
throw new Exception('Missing execution');
|
||||
}
|
||||
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
$project = new Document($payload['project'] ?? []);
|
||||
if ($project->getId() != '6862e6a6000cce69f9da') {
|
||||
$dbForProject->upsertDocument('executions', $execution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Appwrite\Platform\Workers;
|
||||
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Messaging\Status as MessageStatus;
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
@@ -100,9 +102,7 @@ class Messaging extends Action
|
||||
|
||||
$type = $payload['type'] ?? '';
|
||||
|
||||
Span::init('messaging');
|
||||
Span::add('project', $project->getId());
|
||||
Span::add('type', $type);
|
||||
Span::add('message.type', $type);
|
||||
|
||||
try {
|
||||
switch ($type) {
|
||||
@@ -140,12 +140,6 @@ class Messaging extends Action
|
||||
$userIds = $message->getAttribute('users', []);
|
||||
$providerType = $message->getAttribute('providerType');
|
||||
|
||||
Span::add('messageId', $message->getId());
|
||||
Span::add('providerType', $providerType);
|
||||
Span::add('topicsCount', \count($topicIds));
|
||||
Span::add('usersCount', \count($userIds));
|
||||
Span::add('targetsCount', \count($targetIds));
|
||||
|
||||
/**
|
||||
* @var array<Document> $allTargets
|
||||
*/
|
||||
@@ -189,31 +183,13 @@ class Messaging extends Action
|
||||
\array_push($allTargets, ...$targets);
|
||||
}
|
||||
|
||||
Span::add('recipientsTotal', \count($allTargets));
|
||||
|
||||
// Extract country codes for SMS targets
|
||||
if ($providerType === MESSAGE_TYPE_SMS && !empty($allTargets)) {
|
||||
$countryCodes = [];
|
||||
foreach ($allTargets as $target) {
|
||||
$identifier = $target->getAttribute('identifier', '');
|
||||
$countryCode = $this->extractCountryCode($identifier);
|
||||
if ($countryCode !== null) {
|
||||
$countryCodes[$countryCode] = ($countryCodes[$countryCode] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
foreach ($countryCodes as $code => $count) {
|
||||
Span::add('countryCode_' . $code, $count);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($allTargets)) {
|
||||
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
||||
'status' => MessageStatus::FAILED,
|
||||
'deliveryErrors' => ['No valid recipients found.']
|
||||
]));
|
||||
|
||||
Span::add('status', 'failed');
|
||||
Span::add('error', 'No valid recipients found.');
|
||||
Span::add('message.skipped', 'no_valid_recipients');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,14 +204,10 @@ class Messaging extends Action
|
||||
'deliveryErrors' => ['No enabled provider found.']
|
||||
]));
|
||||
|
||||
Span::add('status', 'failed');
|
||||
Span::add('error', 'No enabled provider found.');
|
||||
Span::add('message.skipped', 'no_enabled_provider');
|
||||
return;
|
||||
}
|
||||
|
||||
Span::add('provider', $default->getAttribute('provider'));
|
||||
Span::add('providerName', $default->getAttribute('name'));
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, null>> $identifiers
|
||||
*/
|
||||
@@ -377,14 +349,12 @@ class Messaging extends Action
|
||||
|
||||
if (\count($message->getAttribute('deliveryErrors')) > 0) {
|
||||
$message->setAttribute('status', MessageStatus::FAILED);
|
||||
Span::add('status', 'failed');
|
||||
} else {
|
||||
$message->setAttribute('status', MessageStatus::SENT);
|
||||
Span::add('status', 'sent');
|
||||
}
|
||||
|
||||
Span::add('deliveredTotal', $deliveredTotal);
|
||||
Span::add('errorsTotal', \count($deliveryErrors));
|
||||
Span::add('message.delivered_total', $deliveredTotal);
|
||||
Span::add('message.errors_total', \count($deliveryErrors));
|
||||
|
||||
$message->removeAttribute('to');
|
||||
|
||||
@@ -431,22 +401,12 @@ class Messaging extends Action
|
||||
|
||||
private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Log $log): void
|
||||
{
|
||||
Span::add('providerType', 'sms');
|
||||
|
||||
// Extract country code from the single recipient phone number
|
||||
$countryCode = $this->extractCountryCode($recipients[0] ?? '');
|
||||
if ($countryCode !== null) {
|
||||
Span::add('countryCode', $countryCode);
|
||||
}
|
||||
|
||||
if ($this->adapter === null) {
|
||||
$this->adapter = $this->createInternalSMSAdapter();
|
||||
}
|
||||
|
||||
if ($this->adapter === null) {
|
||||
Span::add('status', 'skipped');
|
||||
Span::add('warning', 'SMS adapter is not set.');
|
||||
return;
|
||||
throw new \Exception('SMS adapter is not set.');
|
||||
}
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
@@ -456,13 +416,19 @@ class Messaging extends Action
|
||||
$denyList = System::getEnv('_APP_SMS_PROJECTS_DENY_LIST', '');
|
||||
$denyList = explode(',', $denyList);
|
||||
if (\in_array($project->getId(), $denyList)) {
|
||||
Span::add('status', 'denied');
|
||||
Span::add('error', 'Project is in the deny list.');
|
||||
Span::add('message.skipped', 'project_denied');
|
||||
return;
|
||||
}
|
||||
|
||||
$from = System::getEnv('_APP_SMS_FROM', '');
|
||||
Span::add('from', $from);
|
||||
Span::add('message.from', $from);
|
||||
|
||||
try {
|
||||
$phoneNumber = PhoneNumberUtil::getInstance()->parse($recipients[0] ?? '');
|
||||
Span::add('message.country_code', $phoneNumber->getCountryCode());
|
||||
} catch (NumberParseException $e) {
|
||||
Span::add('message.country_code', 'unknown');
|
||||
}
|
||||
|
||||
$sms = new SMS(
|
||||
$recipients,
|
||||
@@ -470,14 +436,7 @@ class Messaging extends Action
|
||||
$from
|
||||
);
|
||||
|
||||
try {
|
||||
$result = $this->adapter->send($sms);
|
||||
Span::add('status', 'sent');
|
||||
Span::add('deliveredTo', $result['deliveredTo'] ?? 0);
|
||||
} catch (\Throwable $th) {
|
||||
Span::add('status', 'failed');
|
||||
throw new \Exception('Failed sending to targets with error: ' . $th->getMessage());
|
||||
}
|
||||
$this->adapter->send($sms);
|
||||
}
|
||||
|
||||
|
||||
@@ -749,7 +708,6 @@ class Messaging extends Action
|
||||
private function createInternalSMSAdapter(): ?SMSAdapter
|
||||
{
|
||||
if (empty(System::getEnv('_APP_SMS_PROVIDER')) || empty(System::getEnv('_APP_SMS_FROM'))) {
|
||||
Span::add('warning', 'Skipped SMS processing. Missing "_APP_SMS_PROVIDER" or "_APP_SMS_FROM" environment variables.');
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -795,13 +753,11 @@ class Messaging extends Action
|
||||
$provider = $this->createProviderFromDSN($localDSN);
|
||||
$adapter = $this->getSmsAdapter($provider);
|
||||
} catch (\Exception) {
|
||||
Span::add('warning', 'Unable to create adapter: ' . $localDSN->getHost());
|
||||
continue;
|
||||
}
|
||||
|
||||
$callingCode = $localDSN->getParam('local', '');
|
||||
if (empty($callingCode)) {
|
||||
Span::add('warning', 'Unable to register adapter: ' . $localDSN->getHost() . '. Missing `local` parameter.');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -872,34 +828,4 @@ class Messaging extends Action
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract country calling code from a phone number using known country codes.
|
||||
*/
|
||||
private function extractCountryCode(string $phoneNumber): ?string
|
||||
{
|
||||
if (!\str_starts_with($phoneNumber, '+')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$number = \substr($phoneNumber, 1);
|
||||
|
||||
if (empty($number)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phoneCodes = Config::getParam('locale-phones', []);
|
||||
$codes = \array_unique(\array_values($phoneCodes));
|
||||
|
||||
// Sort by length descending to match longest codes first (e.g., 1868 before 1)
|
||||
\usort($codes, fn ($a, $b) => \strlen($b) - \strlen($a));
|
||||
|
||||
foreach ($codes as $code) {
|
||||
if (\str_starts_with($number, $code)) {
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class Redis implements Adapter
|
||||
|
||||
public function ping($message = null): bool
|
||||
{
|
||||
return $this->client->ping($message);
|
||||
return (bool) $this->client->ping($message);
|
||||
}
|
||||
|
||||
public function subscribe($channels, $callback)
|
||||
|
||||
Reference in New Issue
Block a user