Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller

This commit is contained in:
Prateek Banga
2023-10-10 19:52:19 +05:30
110 changed files with 2101 additions and 780 deletions
+17 -15
View File
@@ -20,8 +20,9 @@ class Linkedin extends OAuth2
* @var array
*/
protected array $scopes = [
'r_liteprofile',
'r_emailaddress',
'openid',
'profile',
'email'
];
/**
@@ -117,8 +118,7 @@ class Linkedin extends OAuth2
public function getUserID(string $accessToken): string
{
$user = $this->getUser($accessToken);
return $user['id'] ?? '';
return $user['sub'] ?? '';
}
/**
@@ -128,9 +128,8 @@ class Linkedin extends OAuth2
*/
public function getUserEmail(string $accessToken): string
{
$email = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
return $email['elements'][0]['handle~']['emailAddress'] ?? '';
$user = $this->getUser($accessToken);
return $user['email'] ?? '';
}
/**
@@ -144,9 +143,8 @@ class Linkedin extends OAuth2
*/
public function isEmailVerified(string $accessToken): bool
{
$email = $this->getUserEmail($accessToken);
return !empty($email);
$user = $this->getUser($accessToken);
return $user['email_verified'] ?? false;
}
/**
@@ -159,12 +157,16 @@ class Linkedin extends OAuth2
$user = $this->getUser($accessToken);
$name = '';
if (isset($user['localizedFirstName'])) {
$name = $user['localizedFirstName'];
if (isset($user['name'])) {
return $user['name'];
}
if (isset($user['localizedLastName'])) {
$name = (empty($name)) ? $user['localizedLastName'] : $name . ' ' . $user['localizedLastName'];
if (isset($user['given_name'])) {
$name = $user['given_name'];
}
if (isset($user['family_name'])) {
$name = (empty($name)) ? $user['family_name'] : $name . ' ' . $user['family_name'];
}
return $name;
@@ -178,7 +180,7 @@ class Linkedin extends OAuth2
protected function getUser(string $accessToken)
{
if (empty($this->user)) {
$this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/me', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
$this->user = \json_decode($this->request('GET', 'https://api.linkedin.com/v2/userinfo', ['Authorization: Bearer ' . \urlencode($accessToken)]), true);
}
return $this->user;
+29 -8
View File
@@ -2,19 +2,19 @@
namespace Appwrite\Event;
use Resque;
use Utopia\Database\Document;
use ResqueScheduler;
use Utopia\Database\DateTime;
class Messaging extends Event
{
protected ?string $messageId = null;
private ?string $deliveryTime = null;
public function __construct()
{
parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME);
}
/**
* Sets message ID for the messaging event.
*
@@ -39,17 +39,38 @@ class Messaging extends Event
}
/**
* Executes the event and sends it to the messaging worker.
* Sets Delivery time for the messaging event.
*
* @return string|bool
* @throws \InvalidArgumentException
* @param string $deliveryTime
* @return self
*/
public function trigger(): string|bool
public function setDeliveryTime(string $deliveryTime): self
{
return Resque::enqueue($this->queue, $this->class, [
$this->deliveryTime = $deliveryTime;
return $this;
}
/**
* Returns set Delivery Time for the messaging event.
*
* @return string
*/
public function getDeliveryTime(): string
{
return $this->deliveryTime;
}
/**
* Executes the event and sends it to the messaging worker.
*/
public function trigger(): string | bool
{
ResqueScheduler::enqueueAt(!empty($this->deliveryTime) ? $this->deliveryTime : DateTime::now(), $this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'messageId' => $this->messageId,
]);
return true;
}
}
-6
View File
@@ -45,12 +45,6 @@ class Event extends Validator
* Identify all sections of the pattern.
*/
$type = $parts[0] ?? false;
if ($type == 'functions') {
$this->message = 'Triggering a function on a function event is not allowed.';
return false;
}
$resource = $parts[1] ?? false;
$hasSubResource = $count > 3 && ($events[$type]['$resource'] ?? false) && ($events[$type][$parts[2]]['$resource'] ?? false);
$hasSubSubResource = $count > 5 && $hasSubResource && ($events[$type][$parts[2]][$parts[4]]['$resource'] ?? false);
@@ -0,0 +1,25 @@
<?php
namespace Appwrite\Event\Validator;
use Utopia\Config\Config;
class FunctionEvent extends Event
{
/**
* Is valid.
*
* @param mixed $value
*
* @return bool
*/
public function isValid($value): bool
{
if (str_starts_with($value, 'functions.')) {
$this->message = 'Triggering a function on a function event is not allowed.';
return false;
}
return parent::isValid($value);
}
}
+1
View File
@@ -235,6 +235,7 @@ class Mapper
case 'Utopia\Validator\Domain':
case 'Appwrite\Network\Validator\Email':
case 'Appwrite\Event\Validator\Event':
case 'Appwrite\Event\Validator\FunctionEvent':
case 'Utopia\Validator\HexColor':
case 'Utopia\Validator\Host':
case 'Utopia\Validator\IP':
+3
View File
@@ -67,6 +67,9 @@ abstract class Migration
'1.4.0' => 'V19',
'1.4.1' => 'V19',
'1.4.2' => 'V19',
'1.4.3' => 'V19',
'1.4.4' => 'V19',
'1.4.5' => 'V19',
];
/**
+7 -1
View File
@@ -93,6 +93,12 @@ class Doctor extends Action
Console::log('🟢 HTTPS force option is enabled');
}
if ('enabled' !== App::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled')) {
Console::log('🔴 HTTPS force option is disabled for function domains');
} else {
Console::log('🟢 HTTPS force option is enabled for function domains');
}
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
@@ -245,7 +251,7 @@ class Doctor extends Action
try {
if (App::isProduction()) {
Console::log('');
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost') . '/v1/health/version'), true);
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost') . '/version'), true);
if ($version && isset($version['version'])) {
if (\version_compare($version['version'], App::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) {
+53 -21
View File
@@ -97,6 +97,32 @@ class Hamster extends Action
/** Get Project Name */
$statsPerProject['project_name'] = $project->getAttribute('name');
/** Total Project Variables */
$statsPerProject['custom_variables'] = $dbForProject->count('variables', [], APP_LIMIT_COUNT);
/** Total Migrations */
$statsPerProject['custom_migrations'] = $dbForProject->count('migrations', [], APP_LIMIT_COUNT);
/** Get Custom SMTP */
$smtp = $project->getAttribute('smtp', null);
if ($smtp) {
$statsPerProject['custom_smtp_status'] = $smtp['enabled'] === true ? 'enabled' : 'disabled';
/** Get Custom Templates Count */
$templates = array_keys($project->getAttribute('templates', []));
$statsPerProject['custom_email_templates'] = array_filter($templates, function ($template) {
return str_contains($template, 'email');
});
$statsPerProject['custom_sms_templates'] = array_filter($templates, function ($template) {
return str_contains($template, 'sms');
});
}
/** Get total relationship attributes */
$statsPerProject['custom_relationship_attributes'] = $dbForProject->count('attributes', [
Query::equal('type', ['relationship'])
], APP_LIMIT_COUNT);
/** Get Total Functions */
$statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
@@ -108,6 +134,17 @@ class Hamster extends Action
/** Get Total Deployments */
$statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
$statsPerProject['custom_deployments_manual'] = $dbForProject->count('deployments', [
Query::equal('type', ['manual'])
], APP_LIMIT_COUNT);
$statsPerProject['custom_deployments_git'] = $dbForProject->count('deployments', [
Query::equal('type', ['vcs'])
], APP_LIMIT_COUNT);
/** Get VCS repos connected */
$statsPerProject['custom_vcs_repositories'] = $dbForConsole->count('repositories', [
Query::equal('projectInternalId', [$project->getInternalId()])
], APP_LIMIT_COUNT);
/** Get Total Teams */
$statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT);
@@ -132,19 +169,16 @@ class Hamster extends Action
throw new Exception('Membership not found. Skipping project : ' . $project->getId());
}
$userInternalId = $membership->getAttribute('userInternalId', null);
if ($userInternalId) {
$user = $dbForConsole->findOne('users', [
Query::equal('_id', [$userInternalId]),
]);
$userId = $membership->getAttribute('userId', null);
if ($userId) {
$user = $dbForConsole->getDocument('users', $userId);
$statsPerProject['email'] = $user->getAttribute('email', null);
$statsPerProject['name'] = $user->getAttribute('name', null);
}
}
/** Get Domains */
$statsPerProject['custom_domains'] = $dbForConsole->count('domains', [
$statsPerProject['custom_domains'] = $dbForConsole->count('rules', [
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(APP_LIMIT_COUNT)
]);
@@ -234,15 +268,16 @@ class Hamster extends Action
if (!$res) {
Console::error('Failed to create user profile for project: ' . $project->getId());
}
}
$event = new Event();
$event
->setName('Project Daily Usage')
->setProps($statsPerProject);
$res = $this->mixpanel->createEvent($event);
if (!$res) {
Console::error('Failed to create event for project: ' . $project->getId());
}
$event = new Event();
$event
->setName('Project Daily Usage')
->setProps($statsPerProject);
$res = $this->mixpanel->createEvent($event);
if (!$res) {
Console::error('Failed to create event for project: ' . $project->getId());
}
} catch (Exception $e) {
Console::error('Failed to send stats for project: ' . $project->getId());
@@ -362,12 +397,9 @@ class Hamster extends Action
throw new Exception('Membership not found. Skipping organization : ' . $document->getId());
}
$userInternalId = $membership->getAttribute('userInternalId', null);
if ($userInternalId) {
$user = $dbForConsole->findOne('users', [
Query::equal('_id', [$userInternalId]),
]);
$userId = $membership->getAttribute('userId', null);
if ($userId) {
$user = $dbForConsole->getDocument('users', $userId);
$statsPerOrganization['email'] = $user->getAttribute('email', null);
}
+6 -1
View File
@@ -89,7 +89,12 @@ class Schedule extends Action
$sum = count($results);
$total = $total + $sum;
foreach ($results as $document) {
$schedules[$document['resourceId']] = $getSchedule($document);
try {
$schedules[$document['resourceId']] = $getSchedule($document);
} catch (\Throwable $th) {
Console::error("Failed to load schedule for project {$document['projectId']} and function {$document['resourceId']}");
Console::error($th->getMessage());
}
}
$latestDocument = !empty(array_key_last($results)) ? $results[array_key_last($results)] : null;
+1 -1
View File
@@ -253,7 +253,7 @@ class Specs extends Action
->setParam('docs.url', $endpoint . '/docs');
if ($mocks) {
$path = __DIR__ . '/../config/specs/' . $format . '-mocks-' . $platform . '.json';
$path = __DIR__ . '/../../../../app/config/specs/' . $format . '-mocks-' . $platform . '.json';
if (!file_put_contents($path, json_encode($specs->parse()))) {
throw new Exception('Failed to save mocks spec file: ' . $path);
@@ -32,11 +32,18 @@ class Message extends Any
])
->addRule('deliveryTime', [
'type' => self::TYPE_DATETIME,
'description' => 'Time the message is delivered at.',
'description' => 'The scheduled time for message.',
'required' => false,
'default' => DateTime::now(),
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('deliveredAt', [
'type' => self::TYPE_DATETIME,
'description' => 'The time when the message was delivered.',
'required' => false,
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('deliveryErrors', [
'type' => self::TYPE_STRING,
'description' => 'Delivery errors if any.',
@@ -68,6 +68,7 @@ class Migration extends Model
->addRule('errors', [
'type' => self::TYPE_STRING,
'description' => 'All errors that occurred during the migration process.',
'array' => true,
'default' => [],
'example' => [],
])
+9 -4
View File
@@ -70,7 +70,7 @@ class Executor
array $variables = [],
string $command = null,
) {
$runtimeId = "$projectId-$deploymentId";
$runtimeId = "$projectId-$deploymentId-build";
$route = "/runtimes";
$params = [
'runtimeId' => $runtimeId,
@@ -113,7 +113,7 @@ class Executor
) {
$timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
$runtimeId = "$projectId-$deploymentId";
$runtimeId = "$projectId-$deploymentId-build";
$route = "/runtimes/{$runtimeId}/logs";
$params = [
'timeout' => $timeout
@@ -177,6 +177,7 @@ class Executor
string $method,
array $headers,
string $runtimeEntrypoint = null,
int $requestTimeout = null
) {
if (empty($headers['host'])) {
$headers['host'] = App::getEnv('_APP_DOMAIN', '');
@@ -202,9 +203,13 @@ class Executor
'runtimeEntrypoint' => $runtimeEntrypoint,
];
$timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
// Safety timeout. Executor has timeout, and open runtime has soft timeout.
// This one shouldn't really happen, but prevents from unexpected networking behaviours.
if ($requestTimeout == null) {
$requestTimeout = $timeout + 15;
}
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout);
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $requestTimeout);
$status = $response['headers']['status-code'];
if ($status >= 400) {