mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
chore: bump PHPStan to level 4 and fix all new errors
Raises `phpstan.neon` level from 3 to 4 and fixes the 549 new errors
that level 4 surfaces across 157 files. Fixes are root-cause — no
`@phpstan-ignore`, no `@var` casts, no baseline entries, no widened
types. A handful of latent bugs were fixed along the way:
- `app/controllers/general.php`: path-traversal guard was negating
`\substr(...)` before the strict comparison (`!\substr(...) === $base`
was always `false === $base`). Rewritten as `\substr(...) !== $base`.
- `src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php`
and `.../TablesDB/Logs/XList.php`: were importing the raw Matomo
`DeviceDetector` (whose `getDevice()` returns `?int`) but treating the
result as an array with `deviceName/deviceBrand/deviceModel` keys.
Swapped to `Appwrite\Detector\Detector`, matching the wrapper already
used a few lines below for `$os`/`$client`.
- `src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`: a match
key was checking `$resourceKey === 'functions'` when `$resourceKey`
is `'functionId'|'siteId'` — always false. Switched to the intended
`$resource->getCollection() === 'functions'` check.
- `src/Appwrite/OpenSSL/OpenSSL.php`: `encrypt()` return type tightened
to `string|false` to match `openssl_encrypt`; this lets callers'
`=== false` error handling remain meaningful.
- `app/controllers/api/messaging.php`: removed a dead
`array_key_exists('from', [])` branch in the Msg91 provider (empty
array literal; branch was unreachable).
Large cleanup categories across the 549 fixes:
- Removed redundant `?? default` on array offsets and expressions that
PHPStan now knows are non-nullable.
- Removed unreachable statements (mostly `return;` after `throw` or
`markTestSkipped()`).
- Removed redundant `is_array`/`is_string`/`is_bool`/`instanceof` checks
on already-narrowed types.
- Added `default =>` arms (or throwing arms) to non-exhaustive matches
on `string`/`mixed` input.
- Removed dead `$document === false` branches where method return types
were tightened to non-nullable `Document`.
- Removed unused properties (`$version` on Etsy/Zoom OAuth2, `$paths` on
Installer State, `$source` on MigrationsWorker, `$account2` on two
GraphQL auth tests), unused traits (`ApiVectorsDB`, `DatabaseFixture`),
and an unused `cleanupStaleExecutions` task method.
- Replaced `assertTrue(true)` and redundant `assertIsArray`/`assertIsString`/
`assertNotNull` assertions with `addToAssertionCount(1)` or
`assertNotEmpty` where the runtime type was already known.
This commit is contained in:
@@ -133,9 +133,6 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
});
|
||||
|
||||
$provider = match ($verifiedToken->getAttribute('type')) {
|
||||
TOKEN_TYPE_VERIFICATION,
|
||||
TOKEN_TYPE_RECOVERY,
|
||||
TOKEN_TYPE_INVITE => SESSION_PROVIDER_EMAIL,
|
||||
TOKEN_TYPE_MAGIC_URL => SESSION_PROVIDER_MAGIC_URL,
|
||||
TOKEN_TYPE_PHONE => SESSION_PROVIDER_PHONE,
|
||||
TOKEN_TYPE_OAUTH2 => $oauthProvider,
|
||||
@@ -335,15 +332,15 @@ Http::post('/v1/account')
|
||||
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
|
||||
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
|
||||
}
|
||||
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
|
||||
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
|
||||
}
|
||||
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
|
||||
throw new Exception(Exception::USER_EMAIL_FREE);
|
||||
}
|
||||
|
||||
@@ -837,7 +834,7 @@ Http::patch('/v1/account/sessions/:sessionId')
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if (!empty($provider) && $className !== null && \class_exists($className)) {
|
||||
if (!empty($provider) && \class_exists($className)) {
|
||||
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
|
||||
@@ -1604,7 +1601,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
}
|
||||
}
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
|
||||
if ($user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
|
||||
if (empty($email)) {
|
||||
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
|
||||
}
|
||||
@@ -1621,7 +1618,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
}
|
||||
|
||||
// If user is not found, check if there is a user with the same email
|
||||
if ($user === false || $user->isEmpty()) {
|
||||
if ($user->isEmpty()) {
|
||||
$userWithEmail = $dbForProject->findOne('users', [
|
||||
Query::equal('email', [$email]),
|
||||
]);
|
||||
@@ -1634,7 +1631,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
}
|
||||
|
||||
// If user is not found, check if there is an identity with the same email
|
||||
if ($user === false || $user->isEmpty()) {
|
||||
if ($user->isEmpty()) {
|
||||
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
|
||||
Query::equal('providerEmail', [$email]),
|
||||
]);
|
||||
@@ -1646,7 +1643,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
}
|
||||
}
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // Last option -> create the user
|
||||
if ($user->isEmpty()) { // Last option -> create the user
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0) {
|
||||
@@ -1679,15 +1676,15 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$failureRedirect(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
|
||||
$failureRedirect(Exception::USER_EMAIL_DISPOSABLE);
|
||||
}
|
||||
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
|
||||
$failureRedirect(Exception::USER_EMAIL_NOT_CANONICAL);
|
||||
}
|
||||
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
|
||||
$failureRedirect(Exception::USER_EMAIL_FREE);
|
||||
}
|
||||
|
||||
@@ -1820,15 +1817,15 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$failureRedirect(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
|
||||
$failureRedirect(Exception::USER_EMAIL_DISPOSABLE);
|
||||
}
|
||||
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
|
||||
$failureRedirect(Exception::USER_EMAIL_NOT_CANONICAL);
|
||||
}
|
||||
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
|
||||
$failureRedirect(Exception::USER_EMAIL_FREE);
|
||||
}
|
||||
|
||||
@@ -1954,7 +1951,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->addCookie($store->getKey(), $encoded, (new \DateTime($expire))->getTimestamp(), '/', $cookieDomain, ('https' == $protocol), true, Config::getParam('cookieSamesite'));
|
||||
}
|
||||
|
||||
if (isset($sessionUpgrade) && $sessionUpgrade && isset($session)) {
|
||||
if (isset($sessionUpgrade) && isset($session)) {
|
||||
foreach ($user->getAttribute('targets', []) as $target) {
|
||||
if ($target->getAttribute('providerType') !== MESSAGE_TYPE_PUSH) {
|
||||
continue;
|
||||
@@ -2178,15 +2175,15 @@ Http::post('/v1/account/tokens/magic-url')
|
||||
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
|
||||
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
|
||||
}
|
||||
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
|
||||
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
|
||||
}
|
||||
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
|
||||
throw new Exception(Exception::USER_EMAIL_FREE);
|
||||
}
|
||||
|
||||
@@ -2488,15 +2485,15 @@ Http::post('/v1/account/tokens/email')
|
||||
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
|
||||
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
|
||||
}
|
||||
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
|
||||
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
|
||||
}
|
||||
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
|
||||
throw new Exception(Exception::USER_EMAIL_FREE);
|
||||
}
|
||||
|
||||
@@ -3397,15 +3394,15 @@ Http::patch('/v1/account/email')
|
||||
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
|
||||
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
|
||||
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
|
||||
}
|
||||
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
|
||||
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
|
||||
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
|
||||
}
|
||||
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
|
||||
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
|
||||
throw new Exception(Exception::USER_EMAIL_FREE);
|
||||
}
|
||||
|
||||
@@ -3442,7 +3439,7 @@ Http::patch('/v1/account/email')
|
||||
*/
|
||||
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
|
||||
|
||||
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||
if (!$oldTarget->isEmpty()) {
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
|
||||
}
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
@@ -3531,7 +3528,7 @@ Http::patch('/v1/account/phone')
|
||||
*/
|
||||
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
|
||||
|
||||
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||
if (!$oldTarget->isEmpty()) {
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
|
||||
}
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
@@ -482,7 +482,6 @@ Http::post('/v1/messaging/providers/msg91')
|
||||
$enabled === true
|
||||
&& \array_key_exists('senderId', $credentials)
|
||||
&& \array_key_exists('authKey', $credentials)
|
||||
&& \array_key_exists('from', $options)
|
||||
) {
|
||||
$enabled = true;
|
||||
} else {
|
||||
@@ -3207,10 +3206,6 @@ Http::post('/v1/messaging/messages/email')
|
||||
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||
}
|
||||
|
||||
if ($status === MessageStatus::SCHEDULED && \is_null($scheduledAt)) {
|
||||
throw new Exception(Exception::MESSAGE_MISSING_SCHEDULE);
|
||||
}
|
||||
|
||||
$mergedTargets = \array_merge($targets, $cc, $bcc);
|
||||
|
||||
if (!empty($mergedTargets)) {
|
||||
@@ -3386,10 +3381,6 @@ Http::post('/v1/messaging/messages/sms')
|
||||
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||
}
|
||||
|
||||
if ($status === MessageStatus::SCHEDULED && \is_null($scheduledAt)) {
|
||||
throw new Exception(Exception::MESSAGE_MISSING_SCHEDULE);
|
||||
}
|
||||
|
||||
if (!empty($targets)) {
|
||||
$foundTargets = $dbForProject->find('targets', [
|
||||
Query::equal('$id', $targets),
|
||||
@@ -3527,10 +3518,6 @@ Http::post('/v1/messaging/messages/push')
|
||||
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||
}
|
||||
|
||||
if ($status === MessageStatus::SCHEDULED && \is_null($scheduledAt)) {
|
||||
throw new Exception(Exception::MESSAGE_MISSING_SCHEDULE);
|
||||
}
|
||||
|
||||
if (!empty($targets)) {
|
||||
$foundTargets = $dbForProject->find('targets', [
|
||||
Query::equal('$id', $targets),
|
||||
@@ -4660,7 +4647,7 @@ Http::delete('/v1/messaging/messages/:messageId')
|
||||
if (!empty($scheduleId)) {
|
||||
try {
|
||||
$dbForPlatform->deleteDocument('schedules', $scheduleId);
|
||||
} catch (Exception) {
|
||||
} catch (\Throwable) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ function getDatabaseTransferResourceServices(string $databaseType)
|
||||
DATABASE_TYPE_LEGACY,
|
||||
DATABASE_TYPE_TABLESDB => Transfer::GROUP_DATABASES_TABLES_DB,
|
||||
DATABASE_TYPE_VECTORSDB => Transfer::GROUP_DATABASES_VECTOR_DB,
|
||||
DATABASE_TYPE_DOCUMENTSDB => Transfer::GROUP_DATABASES_DOCUMENTS_DB
|
||||
DATABASE_TYPE_DOCUMENTSDB => Transfer::GROUP_DATABASES_DOCUMENTS_DB,
|
||||
default => throw new \LogicException('Unknown database type: ' . $databaseType),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -113,11 +113,12 @@ Http::get('/v1/project/usage')
|
||||
$factor = match ($period) {
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
default => throw new \LogicException('Unsupported period: ' . $period),
|
||||
};
|
||||
|
||||
$limit = match ($period) {
|
||||
'1h' => (new DateTime($startDate))->diff(new DateTime($endDate))->days * 24,
|
||||
'1d' => (new DateTime($startDate))->diff(new DateTime($endDate))->days
|
||||
'1d' => (new DateTime($startDate))->diff(new DateTime($endDate))->days,
|
||||
};
|
||||
|
||||
$format = match ($period) {
|
||||
|
||||
@@ -321,7 +321,6 @@ Http::patch('/v1/projects/:projectId/auth/:method')
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
$auth = Config::getParam('auth')[$method] ?? [];
|
||||
$authKey = $auth['key'] ?? '';
|
||||
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
|
||||
@@ -1535,7 +1535,7 @@ Http::patch('/v1/users/:userId/email')
|
||||
Query::equal('identifier', [$email]),
|
||||
]);
|
||||
|
||||
if ($target instanceof Document && !$target->isEmpty()) {
|
||||
if (!$target->isEmpty()) {
|
||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
@@ -1600,7 +1600,7 @@ Http::patch('/v1/users/:userId/email')
|
||||
*/
|
||||
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
|
||||
|
||||
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||
if (!$oldTarget->isEmpty()) {
|
||||
if (\strlen($email) !== 0) {
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), new Document(['identifier' => $email]));
|
||||
$oldTarget->setAttribute('identifier', $email);
|
||||
@@ -1681,7 +1681,7 @@ Http::patch('/v1/users/:userId/phone')
|
||||
Query::equal('identifier', [$number]),
|
||||
]);
|
||||
|
||||
if ($target instanceof Document && !$target->isEmpty()) {
|
||||
if (!$target->isEmpty()) {
|
||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
@@ -1696,7 +1696,7 @@ Http::patch('/v1/users/:userId/phone')
|
||||
*/
|
||||
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
|
||||
|
||||
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||
if (!$oldTarget->isEmpty()) {
|
||||
if ($number !== '') {
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), new Document(['identifier' => $number]));
|
||||
$oldTarget->setAttribute('identifier', $number);
|
||||
@@ -2842,6 +2842,7 @@ Http::get('/v1/users/usage')
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new \LogicException('Unsupported period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
+30
-47
@@ -67,7 +67,7 @@ Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, Bus $bus, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeleteEvent $queueForDeletes, int $executionsRetentionCount)
|
||||
{
|
||||
$host = $request->getHostname() ?? '';
|
||||
$host = $request->getHostname();
|
||||
if (!empty($previewHostname)) {
|
||||
$host = $previewHostname;
|
||||
}
|
||||
@@ -200,12 +200,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
$deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('resourceType', '') === 'functions') {
|
||||
$type = 'function';
|
||||
} elseif ($deployment->getAttribute('resourceType', '') === 'sites') {
|
||||
$type = 'site';
|
||||
}
|
||||
|
||||
if ($deployment->isEmpty()) {
|
||||
$resourceType = $rule->getAttribute('deploymentResourceType', '');
|
||||
$resourceId = $rule->getAttribute('deploymentResourceId', '');
|
||||
@@ -215,6 +209,14 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('resourceType', '') === 'functions') {
|
||||
$type = 'function';
|
||||
} elseif ($deployment->getAttribute('resourceType', '') === 'sites') {
|
||||
$type = 'site';
|
||||
} else {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown deployment resource type', view: $errorView);
|
||||
}
|
||||
|
||||
$resource = $type === 'function' ?
|
||||
$authorization->skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
|
||||
$authorization->skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
|
||||
@@ -302,13 +304,13 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
}
|
||||
}
|
||||
|
||||
$body = $swooleRequest->getContent() ?? '';
|
||||
$body = $swooleRequest->getContent() ?: '';
|
||||
$method = $swooleRequest->server['request_method'];
|
||||
|
||||
$requestHeaders = $request->getHeaders();
|
||||
|
||||
if ($resource->isEmpty() || !$resource->getAttribute('enabled')) {
|
||||
if ($type === 'functions') {
|
||||
if ($type === 'function') {
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND, view: $errorView);
|
||||
} else {
|
||||
throw new AppwriteException(AppwriteException::SITE_NOT_FOUND, view: $errorView);
|
||||
@@ -330,7 +332,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
$runtime = match ($type) {
|
||||
'function' => $runtimes[$resource->getAttribute('runtime')] ?? null,
|
||||
'site' => $runtimes[$resource->getAttribute('buildRuntime')] ?? null,
|
||||
default => null
|
||||
};
|
||||
|
||||
// Static site enforced runtime
|
||||
@@ -459,10 +460,10 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
// V2 vars
|
||||
if ($version === 'v2') {
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'],
|
||||
'APPWRITE_FUNCTION_DATA' => $body,
|
||||
'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? ''
|
||||
'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'],
|
||||
'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt']
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -678,9 +679,8 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
|
||||
if (\is_string($logs) && \strlen($logs) > $maxLogLength) {
|
||||
$warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = max(0, $maxLogLength - $warningLength);
|
||||
$logs = $warningMessage . ($maxContentLength > 0 ? \substr($logs, -$maxContentLength) : '');
|
||||
$maxContentLength = $maxLogLength - \strlen($warningMessage);
|
||||
$logs = $warningMessage . \substr($logs, -$maxContentLength);
|
||||
}
|
||||
|
||||
// Truncate errors if they exceed the limit
|
||||
@@ -689,9 +689,8 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
|
||||
if (\is_string($errors) && \strlen($errors) > $maxErrorLength) {
|
||||
$warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = max(0, $maxErrorLength - $warningLength);
|
||||
$errors = $warningMessage . ($maxContentLength > 0 ? \substr($errors, -$maxContentLength) : '');
|
||||
$maxContentLength = $maxErrorLength - \strlen($warningMessage);
|
||||
$errors = $warningMessage . \substr($errors, -$maxContentLength);
|
||||
}
|
||||
/** Update execution status */
|
||||
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
|
||||
@@ -719,14 +718,12 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
throw $th;
|
||||
}
|
||||
} finally {
|
||||
if ($type === 'function' || $type === 'site') {
|
||||
$bus->dispatch(new ExecutionCompleted(
|
||||
execution: $execution->getArrayCopy(),
|
||||
project: $project->getArrayCopy(),
|
||||
spec: $spec,
|
||||
resource: $resource->getArrayCopy(),
|
||||
));
|
||||
}
|
||||
$bus->dispatch(new ExecutionCompleted(
|
||||
execution: $execution->getArrayCopy(),
|
||||
project: $project->getArrayCopy(),
|
||||
spec: $spec,
|
||||
resource: $resource->getArrayCopy(),
|
||||
));
|
||||
}
|
||||
|
||||
$execution->setAttribute('logs', '');
|
||||
@@ -774,20 +771,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
deployment: $deployment->getArrayCopy(),
|
||||
));
|
||||
|
||||
/* cleanup */
|
||||
if ($executionsRetentionCount > 0 && ENABLE_EXECUTIONS_LIMIT_ON_ROUTE) {
|
||||
$resourceType = $type === 'function'
|
||||
? RESOURCE_TYPE_FUNCTIONS
|
||||
: RESOURCE_TYPE_SITES;
|
||||
|
||||
$queueForDeletes
|
||||
->setProject($project)
|
||||
->setResourceType($resourceType)
|
||||
->setResource($resource->getSequence())
|
||||
->setType(DELETE_TYPE_EXECUTIONS_LIMIT)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
return true;
|
||||
} elseif ($type === 'api') {
|
||||
return false;
|
||||
@@ -852,7 +835,7 @@ Http::init()
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
$hostname = $request->getHostname() ?? '';
|
||||
$hostname = $request->getHostname();
|
||||
$platformHostnames = $platform['hostnames'] ?? [];
|
||||
// Only run Router when external domain
|
||||
if (!\in_array($hostname, $platformHostnames) || !empty($previewHostname)) {
|
||||
@@ -1499,9 +1482,9 @@ Http::error()
|
||||
->setParam('development', Http::isDevelopment())
|
||||
->setParam('projectName', $project->getAttribute('name'))
|
||||
->setParam('projectURL', $project->getAttribute('url'))
|
||||
->setParam('message', $output['message'] ?? '')
|
||||
->setParam('type', $output['type'] ?? '')
|
||||
->setParam('code', $output['code'] ?? '')
|
||||
->setParam('message', $output['message'])
|
||||
->setParam('type', $output['type'])
|
||||
->setParam('code', $output['code'])
|
||||
->setParam('trace', $output['trace'] ?? [])
|
||||
->setParam('exception', $error);
|
||||
|
||||
@@ -1616,7 +1599,7 @@ Http::get('/.well-known/acme-challenge/*')
|
||||
throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path');
|
||||
}
|
||||
|
||||
if (!\substr($absolute, 0, \strlen($base)) === $base) {
|
||||
if (\substr($absolute, 0, \strlen($base)) !== $base) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, 'Invalid path');
|
||||
}
|
||||
|
||||
@@ -1695,7 +1678,7 @@ Http::get('/_appwrite/authorize')
|
||||
->inject('previewHostname')
|
||||
->action(function (Request $request, Response $response, string $previewHostname) {
|
||||
|
||||
$host = $request->getHostname() ?? '';
|
||||
$host = $request->getHostname();
|
||||
if (!empty($previewHostname)) {
|
||||
$host = $previewHostname;
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ Http::get('/v1/mock/github/callback')
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$projectInternalId = $project->getSequence();
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ use Utopia\Validator\WhiteList;
|
||||
|
||||
$parseLabel = function (string $label, array $responsePayload, array $requestParams, User $user) {
|
||||
preg_match_all('/{(.*?)}/', $label, $matches);
|
||||
foreach ($matches[1] ?? [] as $pos => $match) {
|
||||
foreach ($matches[1] as $pos => $match) {
|
||||
$find = $matches[0][$pos];
|
||||
$parts = explode('.', $match);
|
||||
|
||||
@@ -54,8 +54,8 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "The server encountered an error while parsing the label: $label. Please create an issue on GitHub to allow us to investigate further https://github.com/appwrite/appwrite/issues/new/choose");
|
||||
}
|
||||
|
||||
$namespace = $parts[0] ?? '';
|
||||
$replace = $parts[1] ?? '';
|
||||
$namespace = $parts[0];
|
||||
$replace = $parts[1];
|
||||
|
||||
$params = match ($namespace) {
|
||||
'user' => (array) $user,
|
||||
@@ -263,8 +263,7 @@ Http::init()
|
||||
$userClone->setAttribute('type', match ($apiKey->getType()) {
|
||||
API_KEY_STANDARD => ACTIVITY_TYPE_KEY_PROJECT,
|
||||
API_KEY_ACCOUNT => ACTIVITY_TYPE_KEY_ACCOUNT,
|
||||
API_KEY_ORGANIZATION => ACTIVITY_TYPE_KEY_ORGANIZATION,
|
||||
default => ACTIVITY_TYPE_KEY_PROJECT,
|
||||
default => ACTIVITY_TYPE_KEY_ORGANIZATION,
|
||||
});
|
||||
$auditContext->user = $userClone;
|
||||
}
|
||||
@@ -385,7 +384,7 @@ Http::init()
|
||||
}
|
||||
|
||||
// Step 6: Update project and user last activity
|
||||
if (! $project->isEmpty() && $project->getId() !== 'console') {
|
||||
if ($project->getId() !== 'console') {
|
||||
$accessedAt = $project->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), new Document([
|
||||
@@ -415,9 +414,6 @@ Http::init()
|
||||
}
|
||||
|
||||
// Steps 7-9: Access Control - Method, Namespace and Scope Validation
|
||||
/**
|
||||
* @var ?Method $method
|
||||
*/
|
||||
$method = $route->getLabel('sdk', false);
|
||||
|
||||
// Take the first method if there's more than one,
|
||||
@@ -646,7 +642,7 @@ Http::init()
|
||||
|
||||
if (! empty($data) && ! $cacheLog->isEmpty()) {
|
||||
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
|
||||
$type = $parts[0] ?? null;
|
||||
$type = $parts[0];
|
||||
|
||||
if ($type === 'bucket' && (! $isImageTransformation || ! $isDisabled)) {
|
||||
$bucketId = $parts[1] ?? null;
|
||||
@@ -937,7 +933,7 @@ Http::shutdown()
|
||||
}
|
||||
|
||||
$auditUser = $auditContext->user;
|
||||
if (! empty($auditContext->resource) && ! \is_null($auditUser) && ! $auditUser->isEmpty()) {
|
||||
if (! empty($auditContext->resource) && ! $auditUser->isEmpty()) {
|
||||
/**
|
||||
* audits.payload is switched to default true
|
||||
* in order to auto audit payload for all endpoints
|
||||
|
||||
@@ -85,7 +85,7 @@ return function (Container $container): void {
|
||||
|
||||
return $dbForPlatform->findOne('rules', [
|
||||
Query::equal('domain', [$domain]),
|
||||
]) ?? new Document();
|
||||
]);
|
||||
});
|
||||
|
||||
$permitsCurrentProject = $rule->getAttribute('projectInternalId', '') === $project->getSequence();
|
||||
@@ -139,7 +139,7 @@ return function (Container $container): void {
|
||||
$sdkValidator = new WhiteList($servers, true);
|
||||
$sdk = \strtolower($request->getHeader('x-sdk-name', 'UNKNOWN'));
|
||||
|
||||
if ($sdk !== 'UNKNOWN' && $sdkValidator->isValid($sdk)) {
|
||||
if ($sdk !== 'unknown' && $sdkValidator->isValid($sdk)) {
|
||||
$sdks = $key->getAttribute('sdks', []);
|
||||
|
||||
if (!\in_array($sdk, $sdks, true)) {
|
||||
|
||||
@@ -71,7 +71,7 @@ $register->set('logger', function () {
|
||||
|
||||
$providerConfig = match ($providerName) {
|
||||
'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',],
|
||||
'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''],
|
||||
'logowl' => ['ticket' => $configChunks[0], 'host' => ''],
|
||||
default => ['key' => $providerConfig],
|
||||
};
|
||||
}
|
||||
@@ -249,11 +249,11 @@ $register->set('pools', function () {
|
||||
$poolSize = max(1, (int)($instanceConnections / $workerCount));
|
||||
|
||||
foreach ($connections as $key => $connection) {
|
||||
$type = $connection['type'] ?? '';
|
||||
$multiple = $connection['multiple'] ?? false;
|
||||
$schemes = $connection['schemes'] ?? [];
|
||||
$type = $connection['type'];
|
||||
$multiple = $connection['multiple'];
|
||||
$schemes = $connection['schemes'];
|
||||
$config = [];
|
||||
$dsns = explode(',', $connection['dsns'] ?? '');
|
||||
$dsns = explode(',', $connection['dsns']);
|
||||
foreach ($dsns as &$dsn) {
|
||||
$dsn = explode('=', $dsn);
|
||||
$name = ($multiple) ? $key . '_' . $dsn[0] : $key;
|
||||
@@ -318,7 +318,7 @@ $register->set('pools', function () {
|
||||
));
|
||||
});
|
||||
},
|
||||
'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) {
|
||||
default => function () use ($dsnHost, $dsnPort, $dsnPass) {
|
||||
$redis = new \Redis();
|
||||
@$redis->pconnect($dsnHost, (int)$dsnPort);
|
||||
if ($dsnPass) {
|
||||
@@ -328,7 +328,6 @@ $register->set('pools', function () {
|
||||
|
||||
return $redis;
|
||||
},
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
|
||||
};
|
||||
|
||||
$poolAdapter = System::getEnv('_APP_POOL_ADAPTER', default: 'stack') === 'swoole' ? new SwoolePool() : new StackPool();
|
||||
|
||||
@@ -266,7 +266,7 @@ function getDevice(string $root, string $connection = ''): Device
|
||||
return new Local($root);
|
||||
}
|
||||
} else {
|
||||
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
|
||||
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL))) {
|
||||
case Storage::DEVICE_LOCAL:
|
||||
default:
|
||||
return new Local($root);
|
||||
|
||||
@@ -375,7 +375,7 @@ return function (Container $container): void {
|
||||
|
||||
return $dbForPlatform->findOne('rules', [
|
||||
Query::equal('domain', [$domain]),
|
||||
]) ?? new Document();
|
||||
]);
|
||||
});
|
||||
|
||||
$permitsCurrentProject = $rule->getAttribute('projectInternalId', '') === $project->getSequence();
|
||||
@@ -478,14 +478,10 @@ return function (Container $container): void {
|
||||
}
|
||||
|
||||
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
|
||||
if ($response) { // if in http context - add debug header
|
||||
$response->addHeader('X-Debug-Fallback', 'false');
|
||||
}
|
||||
$response->addHeader('X-Debug-Fallback', 'false');
|
||||
|
||||
if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) {
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'true');
|
||||
}
|
||||
$response->addHeader('X-Debug-Fallback', 'true');
|
||||
$fallback = $request->getHeader('x-fallback-cookies', '');
|
||||
$fallback = \json_decode($fallback, true);
|
||||
$store->decode(((is_array($fallback) && isset($fallback[$store->getKey()])) ? $fallback[$store->getKey()] : ''));
|
||||
@@ -1084,7 +1080,7 @@ return function (Container $container): void {
|
||||
$sdkValidator = new WhiteList($servers, true);
|
||||
$sdk = \strtolower($request->getHeader('x-sdk-name', 'UNKNOWN'));
|
||||
|
||||
if ($sdk !== 'UNKNOWN' && $sdkValidator->isValid($sdk)) {
|
||||
if ($sdk !== 'unknown' && $sdkValidator->isValid($sdk)) {
|
||||
$sdks = $key->getAttribute('sdks', []);
|
||||
|
||||
if (! in_array($sdk, $sdks)) {
|
||||
|
||||
@@ -368,7 +368,7 @@ return function (Container $container): void {
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('verboseType', \get_class($error));
|
||||
$log->addTag('projectId', $project->getId() ?? '');
|
||||
$log->addTag('projectId', $project->getId());
|
||||
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ $worker
|
||||
$log->setAction('appwrite-queue-' . $queueName);
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('projectId', $project->getId() ?? 'n/a');
|
||||
$log->addTag('projectId', $project->getId());
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
parameters:
|
||||
level: 3
|
||||
level: 4
|
||||
tmpDir: .phpstan-cache
|
||||
paths:
|
||||
- src
|
||||
|
||||
@@ -11,11 +11,6 @@ class Etsy extends OAuth2
|
||||
*/
|
||||
private string $endpoint = 'https://api.etsy.com/v3/public';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $version = '2022-07-14';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
||||
@@ -121,7 +121,7 @@ class Podio extends OAuth2
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
return \strval($user['user_id']) ?? '';
|
||||
return \strval($user['user_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,11 +11,6 @@ class Zoom extends OAuth2
|
||||
*/
|
||||
private string $endpoint = 'https://zoom.us';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $version = '2022-03-26';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ class PersonalData extends Password
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->email && strpos($password, explode('@', $this->email)[0] ?? '') !== false) {
|
||||
if ($this->email && strpos($password, explode('@', $this->email)[0]) !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class Service
|
||||
array_walk($ports, function (&$value, $key) {
|
||||
$split = explode(':', $value);
|
||||
$this->service['ports'][
|
||||
(isset($split[0])) ? $split[0] : ''
|
||||
$split[0]
|
||||
] = (isset($split[1])) ? $split[1] : '';
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class Env
|
||||
|
||||
foreach ($data as &$row) {
|
||||
$row = explode('=', $row, 2);
|
||||
$key = (isset($row[0])) ? trim($row[0]) : null;
|
||||
$key = trim($row[0]);
|
||||
$value = (isset($row[1])) ? (function (string $v): string {
|
||||
$v = trim($v);
|
||||
if (
|
||||
|
||||
@@ -459,7 +459,7 @@ class Event
|
||||
/**
|
||||
* Identify all sections of the pattern.
|
||||
*/
|
||||
$type = $parts[0] ?? false;
|
||||
$type = $parts[0];
|
||||
$resource = $parts[1] ?? false;
|
||||
$hasSubResource = $count > 3 && \str_starts_with($parts[3], '[');
|
||||
$hasSubSubResource = $count > 5 && \str_starts_with($parts[5], '[') && $hasSubResource;
|
||||
|
||||
@@ -44,7 +44,7 @@ class Event extends Validator
|
||||
/**
|
||||
* Identify all sections of the pattern.
|
||||
*/
|
||||
$type = $parts[0] ?? false;
|
||||
$type = $parts[0];
|
||||
$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);
|
||||
@@ -61,9 +61,6 @@ class Event extends Validator
|
||||
if ($hasSubSubResource) {
|
||||
$subSubType = $parts[4];
|
||||
$subSubResource = $parts[5];
|
||||
if ($count === 8) {
|
||||
$attribute = $parts[7];
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasSubResource && !$hasSubSubResource) {
|
||||
|
||||
@@ -24,7 +24,7 @@ class Webhook extends Event
|
||||
public function trimPayload(): array
|
||||
{
|
||||
$trimmed = parent::trimPayload();
|
||||
if (isset($this->context)) {
|
||||
if (!empty($this->context)) {
|
||||
$trimmed['context'] = [];
|
||||
}
|
||||
return $trimmed;
|
||||
|
||||
@@ -91,26 +91,20 @@ class Mapper
|
||||
}
|
||||
}
|
||||
|
||||
$responses = $method->getResponses() ?? [];
|
||||
$responses = $method->getResponses();
|
||||
|
||||
// If responses is an array, map each response to its model
|
||||
if (\is_array($responses)) {
|
||||
$models = [];
|
||||
foreach ($responses as $response) {
|
||||
$modelName = $response->getModel();
|
||||
// Map each response to its model
|
||||
$models = [];
|
||||
foreach ($responses as $response) {
|
||||
$modelName = $response->getModel();
|
||||
|
||||
if (\is_array($modelName)) {
|
||||
foreach ($modelName as $name) {
|
||||
$models[] = self::$models[$name];
|
||||
}
|
||||
} else {
|
||||
$models[] = self::$models[$modelName];
|
||||
if (\is_array($modelName)) {
|
||||
foreach ($modelName as $name) {
|
||||
$models[] = self::$models[$name];
|
||||
}
|
||||
} else {
|
||||
$models[] = self::$models[$modelName];
|
||||
}
|
||||
} else {
|
||||
// If single response, get its model and wrap in array
|
||||
$modelName = $responses->getModel();
|
||||
$models = [self::$models[$modelName]];
|
||||
}
|
||||
|
||||
foreach ($models as $model) {
|
||||
|
||||
@@ -370,15 +370,8 @@ class Realtime extends MessagingAdapter
|
||||
$params = $getQueryParam($paramKey);
|
||||
|
||||
if (\array_key_exists($paramKey, $reservedParamExpectedTypes) && $params !== null) {
|
||||
$expectedType = $reservedParamExpectedTypes[$paramKey];
|
||||
$isExpectedType = match ($expectedType) {
|
||||
'array' => \is_array($params),
|
||||
'string' => \is_string($params),
|
||||
default => false,
|
||||
};
|
||||
|
||||
// If the value matches the expected type dont use it the queries
|
||||
if ($isExpectedType) {
|
||||
if (\is_string($params)) {
|
||||
$params = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class OpenSSL
|
||||
* @param string $aad
|
||||
* @param int $tag_length
|
||||
*
|
||||
* @return string
|
||||
* @return string|false
|
||||
*/
|
||||
public static function encrypt($data, $method, $key, $options = 0, $iv = '', ?string &$tag = null, $aad = '', $tag_length = 16)
|
||||
{
|
||||
|
||||
@@ -240,9 +240,7 @@ class Install extends Action
|
||||
$inputValue = trim($inputValue);
|
||||
}
|
||||
if ($storedValue !== $inputValue) {
|
||||
if ($installId !== '') {
|
||||
$state->updateGlobalLock($installId, Server::STATUS_ERROR);
|
||||
}
|
||||
$state->updateGlobalLock($installId, Server::STATUS_ERROR);
|
||||
$this->sendBadRequest($response, $swooleResponse, $wantsStream, 'Installation payload mismatch');
|
||||
return;
|
||||
}
|
||||
@@ -262,16 +260,12 @@ class Install extends Action
|
||||
$incomingHash = $state->hashSensitiveValue($incomingValue);
|
||||
if (isset($stored[$hashField])) {
|
||||
if (!hash_equals((string) $stored[$hashField], $incomingHash)) {
|
||||
if ($installId !== '') {
|
||||
$state->updateGlobalLock($installId, Server::STATUS_ERROR);
|
||||
}
|
||||
$state->updateGlobalLock($installId, Server::STATUS_ERROR);
|
||||
$this->sendBadRequest($response, $swooleResponse, $wantsStream, 'Installation payload mismatch');
|
||||
return;
|
||||
}
|
||||
} elseif (isset($stored[$field]) && $incomingValue !== '' && (string) $stored[$field] !== $incomingValue) {
|
||||
if ($installId !== '') {
|
||||
$state->updateGlobalLock($installId, Server::STATUS_ERROR);
|
||||
}
|
||||
$state->updateGlobalLock($installId, Server::STATUS_ERROR);
|
||||
$this->sendBadRequest($response, $swooleResponse, $wantsStream, 'Installation payload mismatch');
|
||||
return;
|
||||
}
|
||||
@@ -430,7 +424,7 @@ class Install extends Action
|
||||
private function deriveNameFromEmail(string $email): string
|
||||
{
|
||||
$parts = explode('@', $email);
|
||||
$username = $parts[0] ?? '';
|
||||
$username = $parts[0];
|
||||
$cleaned = preg_replace('/[^a-zA-Z0-9]/', '', $username);
|
||||
return ucfirst($cleaned);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Status extends Action
|
||||
}
|
||||
|
||||
$data = $state->readProgressFile($installId);
|
||||
if (is_array($data) && isset($data['payload']) && is_array($data['payload'])) {
|
||||
if (isset($data['payload']) && is_array($data['payload'])) {
|
||||
unset(
|
||||
$data['payload']['opensslKey'],
|
||||
$data['payload']['assistantOpenAIKey'],
|
||||
@@ -54,7 +54,7 @@ class Status extends Action
|
||||
);
|
||||
}
|
||||
// Strip sensitive data from step details
|
||||
if (is_array($data) && isset($data['details']) && is_array($data['details'])) {
|
||||
if (isset($data['details']) && is_array($data['details'])) {
|
||||
foreach ($data['details'] as $stepKey => &$stepDetails) {
|
||||
if (is_array($stepDetails)) {
|
||||
unset($stepDetails['sessionSecret'], $stepDetails['trace']);
|
||||
|
||||
@@ -222,7 +222,7 @@ final class Config
|
||||
*/
|
||||
public function setEnabledDatabases(array $value): void
|
||||
{
|
||||
$filtered = array_values(array_filter($value, fn ($v) => is_string($v) && $v !== ''));
|
||||
$filtered = array_values(array_filter($value, fn ($v) => $v !== ''));
|
||||
if (!empty($filtered)) {
|
||||
$this->enabledDatabases = $filtered;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,11 @@ class State
|
||||
private const int PORT_MIN = 1;
|
||||
private const int PORT_MAX = 65535;
|
||||
|
||||
private array $paths;
|
||||
private bool $bootstrapped = false;
|
||||
private int $lastStaleLockClearAt = 0;
|
||||
|
||||
public function __construct(array $paths)
|
||||
public function __construct()
|
||||
{
|
||||
$this->paths = $paths;
|
||||
}
|
||||
|
||||
public function buildConfig(array $overrides = [], bool $useEnv = true): Config
|
||||
@@ -180,7 +178,7 @@ class State
|
||||
if (!preg_match(self::PATTERN_IPV6_WITH_PORT, $value, $matches)) {
|
||||
return false;
|
||||
}
|
||||
$host = $matches[1] ?? '';
|
||||
$host = $matches[1];
|
||||
$port = $matches[2] ?? null;
|
||||
} else {
|
||||
$parts = explode(':', $value);
|
||||
|
||||
@@ -60,7 +60,7 @@ class Server
|
||||
{
|
||||
$this->initPaths();
|
||||
|
||||
$this->state = new State($this->paths);
|
||||
$this->state = new State();
|
||||
|
||||
if (PHP_SAPI === 'cli') {
|
||||
$this->runCli();
|
||||
|
||||
@@ -47,7 +47,7 @@ class AppDomain extends Validator
|
||||
if (!preg_match(self::PATTERN_IPV6_WITH_PORT, $value, $matches)) {
|
||||
return false;
|
||||
}
|
||||
$host = $matches[1] ?? '';
|
||||
$host = $matches[1];
|
||||
$port = $matches[2] ?? null;
|
||||
} else {
|
||||
$parts = explode(':', $value);
|
||||
|
||||
@@ -86,10 +86,10 @@ class Get extends Action
|
||||
}
|
||||
|
||||
if (!$isEmployee && !empty($githubName)) {
|
||||
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
|
||||
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub'] ?? ''), $employees));
|
||||
if (!empty($employeeGitHub)) {
|
||||
$isEmployee = true;
|
||||
$employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
|
||||
$employeeNumber = $employees[$employeeGitHub]['spot'];
|
||||
$createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +90,10 @@ class Get extends Action
|
||||
}
|
||||
|
||||
if (!$isEmployee && !empty($githubName)) {
|
||||
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
|
||||
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub'] ?? ''), $employees));
|
||||
if (!empty($employeeGitHub)) {
|
||||
$isEmployee = true;
|
||||
$employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
|
||||
$employeeNumber = $employees[$employeeGitHub]['spot'];
|
||||
$createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ class Get extends Action
|
||||
$doc->strictErrorChecking = false;
|
||||
@$doc->loadHTML($res->getBody());
|
||||
|
||||
$links = $doc->getElementsByTagName('link') ?? [];
|
||||
$links = $doc->getElementsByTagName('link');
|
||||
$outputHref = '';
|
||||
$outputExt = '';
|
||||
$space = 0;
|
||||
@@ -128,7 +128,7 @@ class Get extends Action
|
||||
case 'jpeg':
|
||||
$size = \explode('x', \strtolower($sizes));
|
||||
|
||||
$sizeWidth = (int) ($size[0] ?? 0);
|
||||
$sizeWidth = (int) $size[0];
|
||||
$sizeHeight = (int) ($size[1] ?? 0);
|
||||
|
||||
if (($sizeWidth * $sizeHeight) >= $space) {
|
||||
|
||||
@@ -60,7 +60,6 @@ class Get extends Action
|
||||
|
||||
public function action(string $text, int $size, int $margin, bool $download, Response $response)
|
||||
{
|
||||
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
|
||||
$options = new QROptions([
|
||||
'addQuietzone' => true,
|
||||
'quietzoneSize' => $margin,
|
||||
|
||||
@@ -105,7 +105,7 @@ class Get extends Action
|
||||
$client->addHeader('content-type', Client::CONTENT_TYPE_APPLICATION_JSON);
|
||||
|
||||
// Convert indexed array to empty array (should not happen due to Assoc validator)
|
||||
if (is_array($headers) && count($headers) > 0 && array_keys($headers) === range(0, count($headers) - 1)) {
|
||||
if (count($headers) > 0 && array_keys($headers) === range(0, count($headers) - 1)) {
|
||||
$headers = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class Base extends Action
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
$providerRepositoryId = $function->getAttribute('providerRepositoryId', '');
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ class Base extends Action
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
$providerRepositoryId = $site->getAttribute('providerRepositoryId', '');
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ abstract class Action extends DatabasesAction
|
||||
/**
|
||||
* The current API context (either 'table' or 'collection').
|
||||
*/
|
||||
private ?string $context = COLLECTIONS;
|
||||
private string $context = COLLECTIONS;
|
||||
|
||||
/**
|
||||
* Get the response model used in the SDK and HTTP responses.
|
||||
|
||||
+2
-2
@@ -26,9 +26,9 @@ use Utopia\Validator\Range;
|
||||
abstract class Action extends UtopiaAction
|
||||
{
|
||||
/**
|
||||
* @var string|null The current context (either 'column' or 'attribute')
|
||||
* @var string The current context (either 'column' or 'attribute')
|
||||
*/
|
||||
private ?string $context = ATTRIBUTES;
|
||||
private string $context = ATTRIBUTES;
|
||||
|
||||
/**
|
||||
* Get the correct response model.
|
||||
|
||||
+3
-3
@@ -14,10 +14,10 @@ use Utopia\Database\Validator\Authorization;
|
||||
abstract class Action extends DatabasesAction
|
||||
{
|
||||
/**
|
||||
* @var string|null The current context (either 'row' or 'document')
|
||||
* @var string The current context (either 'row' or 'document')
|
||||
*/
|
||||
private ?string $context = DOCUMENTS;
|
||||
private ?string $databaseType = DATABASE_TYPE_LEGACY;
|
||||
private string $context = DOCUMENTS;
|
||||
private string $databaseType = DATABASE_TYPE_LEGACY;
|
||||
|
||||
/**
|
||||
* Get the response model used in the SDK and HTTP responses.
|
||||
|
||||
-10
@@ -293,16 +293,6 @@ class Create extends Action
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
|
||||
}
|
||||
|
||||
if ($permission === Database::PERMISSION_UPDATE) {
|
||||
$validDocument = $authorization->isValid(
|
||||
new Input($permission, $document->getUpdate())
|
||||
);
|
||||
$valid = $validCollection || $validDocument;
|
||||
if ($documentSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
$relationships = \array_filter(
|
||||
$collection->getAttribute('attributes', []),
|
||||
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ class Get extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$selects = Query::groupByType($queries)['selections'] ?? [];
|
||||
$selects = Query::groupByType($queries)['selections'];
|
||||
$collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence();
|
||||
$collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence();
|
||||
|
||||
|
||||
+1
-6
@@ -353,12 +353,7 @@ class Upsert extends Action
|
||||
$collectionsCache = [];
|
||||
|
||||
if (empty($upserted[0])) {
|
||||
if ($transactionId !== null) {
|
||||
// For transactions, get the document with transaction changes applied
|
||||
$upserted[0] = $transactionState->getDocument($database, $collectionTableId, $documentId, $transactionId);
|
||||
} else {
|
||||
$upserted[0] = $dbForDatabases->getDocument($collectionTableId, $documentId);
|
||||
}
|
||||
$upserted[0] = $dbForDatabases->getDocument($collectionTableId, $documentId);
|
||||
}
|
||||
|
||||
$document = $upserted[0];
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ class XList extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$hasSelects = ! empty(Query::groupByType($queries)['selections'] ?? []);
|
||||
$hasSelects = ! empty(Query::groupByType($queries)['selections']);
|
||||
$collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence();
|
||||
// When there are no select queries, relationship loading is skipped on the
|
||||
// underlying find() to avoid pulling related documents the caller did not ask for.
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ abstract class Action extends UtopiaAction
|
||||
/**
|
||||
* The current API context (either 'columnIndex' or 'index').
|
||||
*/
|
||||
private ?string $context = INDEX;
|
||||
private string $context = INDEX;
|
||||
|
||||
/**
|
||||
* Get the response model used in the SDK and HTTP responses.
|
||||
|
||||
@@ -119,6 +119,7 @@ class Get extends Action
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new \LogicException('Unexpected period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Logs;
|
||||
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
@@ -9,7 +10,6 @@ use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use DeviceDetector\DeviceDetector as Detector;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Database\Database;
|
||||
@@ -103,9 +103,9 @@ class XList extends Action
|
||||
$os = $detector->getOS();
|
||||
$client = $detector->getClient();
|
||||
$device = $detector->getDevice();
|
||||
$deviceName = \is_array($device) ? ($device['deviceName'] ?? '') : '';
|
||||
$deviceBrand = \is_array($device) ? ($device['deviceBrand'] ?? '') : '';
|
||||
$deviceModel = \is_array($device) ? ($device['deviceModel'] ?? '') : '';
|
||||
$deviceName = $device['deviceName'] ?? '';
|
||||
$deviceBrand = $device['deviceBrand'] ?? '';
|
||||
$deviceModel = $device['deviceModel'] ?? '';
|
||||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
|
||||
@@ -9,8 +9,8 @@ abstract class Action extends DatabasesAction
|
||||
/**
|
||||
* The current API context (either 'table' or 'collection').
|
||||
*/
|
||||
private ?string $context = COLLECTIONS;
|
||||
private ?string $databaseType = LEGACY;
|
||||
private string $context = COLLECTIONS;
|
||||
private string $databaseType = LEGACY;
|
||||
|
||||
public function getDatabaseType(): string
|
||||
{
|
||||
|
||||
@@ -144,6 +144,7 @@ class Get extends Action
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new \LogicException('Unexpected period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -133,6 +133,7 @@ class XList extends Action
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new \LogicException('Unexpected period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Logs;
|
||||
|
||||
use Appwrite\Detector\Detector;
|
||||
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 as UtopiaResponse;
|
||||
use DeviceDetector\DeviceDetector as Detector;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Database\Database;
|
||||
@@ -97,9 +97,9 @@ class XList extends Action
|
||||
$os = $detector->getOS();
|
||||
$client = $detector->getClient();
|
||||
$device = $detector->getDevice();
|
||||
$deviceName = \is_array($device) ? ($device['deviceName'] ?? '') : '';
|
||||
$deviceBrand = \is_array($device) ? ($device['deviceBrand'] ?? '') : '';
|
||||
$deviceModel = \is_array($device) ? ($device['deviceModel'] ?? '') : '';
|
||||
$deviceName = $device['deviceName'] ?? '';
|
||||
$deviceBrand = $device['deviceBrand'] ?? '';
|
||||
$deviceModel = $device['deviceModel'] ?? '';
|
||||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
|
||||
@@ -98,7 +98,7 @@ class Create extends CreateDocumentAction
|
||||
$error = '';
|
||||
try {
|
||||
$embedResult = $embeddingAgent->embed($text);
|
||||
$embedding = $embedResult['embedding'] ?? [];
|
||||
$embedding = $embedResult['embedding'];
|
||||
$totalDuration += $embedResult['totalDuration'] ?? 0;
|
||||
$totalTokens += $embedResult['tokensProcessed'] ?? 0;
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -54,7 +54,7 @@ class Databases extends Action
|
||||
*/
|
||||
public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, callable $getDatabasesDB, Realtime $queueForRealtime, Log $log): void
|
||||
{
|
||||
$payload = $message->getPayload() ?? [];
|
||||
$payload = $message->getPayload();
|
||||
|
||||
if (empty($payload)) {
|
||||
throw new Exception('Missing payload');
|
||||
|
||||
@@ -116,7 +116,7 @@ class XList extends Base
|
||||
|
||||
$grouped = Query::groupByType($queries);
|
||||
$filterQueries = $grouped['filters'];
|
||||
$selectQueries = $grouped['selections'] ?? [];
|
||||
$selectQueries = $grouped['selections'];
|
||||
|
||||
try {
|
||||
$results = $dbForProject->find('deployments', $queries);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Executions;
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Event\Delete as DeleteEvent;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Extend\Exception;
|
||||
@@ -101,8 +100,6 @@ class Create extends Base
|
||||
->inject('executor')
|
||||
->inject('platform')
|
||||
->inject('authorization')
|
||||
->inject('queueForDeletes')
|
||||
->inject('executionsRetentionCount')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
@@ -129,8 +126,6 @@ class Create extends Base
|
||||
Executor $executor,
|
||||
array $platform,
|
||||
Authorization $authorization,
|
||||
DeleteEvent $queueForDeletes,
|
||||
int $executionsRetentionCount,
|
||||
) {
|
||||
$async = \strval($async) === 'true' || \strval($async) === '1';
|
||||
|
||||
@@ -145,21 +140,8 @@ class Create extends Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array<string, mixed> $headers
|
||||
*/
|
||||
$assocParams = ['headers'];
|
||||
foreach ($assocParams as $assocParam) {
|
||||
if (!empty('headers') && !is_array($$assocParam)) {
|
||||
$$assocParam = \json_decode($$assocParam, true);
|
||||
}
|
||||
}
|
||||
|
||||
$booleanParams = ['async'];
|
||||
foreach ($booleanParams as $booleamParam) {
|
||||
if (!empty($$booleamParam) && !is_bool($$booleamParam)) {
|
||||
$$booleamParam = $$booleamParam === "true" ? true : false;
|
||||
}
|
||||
if (!is_array($headers)) {
|
||||
$headers = \json_decode($headers, true);
|
||||
}
|
||||
|
||||
// 'headers' validator
|
||||
@@ -349,15 +331,6 @@ class Create extends Base
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
|
||||
if ($executionsRetentionCount > 0 && ENABLE_EXECUTIONS_LIMIT_ON_ROUTE) {
|
||||
$queueForDeletes
|
||||
->setProject($project)
|
||||
->setResource($function->getSequence())
|
||||
->setResourceType(RESOURCE_TYPE_FUNCTIONS)
|
||||
->setType(DELETE_TYPE_EXECUTIONS_LIMIT)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_ACCEPTED);
|
||||
$response->dynamic($execution, Response::MODEL_EXECUTION);
|
||||
return;
|
||||
@@ -370,10 +343,10 @@ class Create extends Base
|
||||
// V2 vars
|
||||
if ($version === 'v2') {
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '',
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'],
|
||||
'APPWRITE_FUNCTION_DATA' => $body,
|
||||
'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? ''
|
||||
'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'],
|
||||
'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt']
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -536,15 +509,6 @@ class Create extends Base
|
||||
}
|
||||
}
|
||||
|
||||
if ($executionsRetentionCount > 0 && ENABLE_EXECUTIONS_LIMIT_ON_ROUTE) {
|
||||
$queueForDeletes
|
||||
->setProject($project)
|
||||
->setResource($function->getSequence())
|
||||
->setResourceType(RESOURCE_TYPE_FUNCTIONS)
|
||||
->setType(DELETE_TYPE_EXECUTIONS_LIMIT)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($execution, Response::MODEL_EXECUTION);
|
||||
|
||||
@@ -162,10 +162,6 @@ class Update extends Base
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When connecting to VCS (Version Control System), you need to provide "installationId" and "providerBranch".');
|
||||
}
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (empty($runtime)) {
|
||||
$runtime = $function->getAttribute('runtime');
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ class Get extends Base
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unsupported period "' . $days['period'] . '".'),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Usage;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
@@ -104,6 +105,7 @@ class XList extends Base
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unsupported period "' . $days['period'] . '".'),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -77,11 +77,7 @@ class Delete extends Base
|
||||
}
|
||||
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable === false || $variable->isEmpty()) {
|
||||
if ($variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ class Get extends Base
|
||||
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if (
|
||||
$variable === false ||
|
||||
$variable->isEmpty() ||
|
||||
$variable->getAttribute('resourceInternalId') !== $function->getSequence() ||
|
||||
$variable->getAttribute('resourceType') !== 'function'
|
||||
@@ -74,10 +73,6 @@ class Get extends Base
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable === false || $variable->isEmpty()) {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class Update extends Base
|
||||
}
|
||||
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function') {
|
||||
if ($variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class Builds extends Action
|
||||
): void {
|
||||
Console::log('Build action started');
|
||||
|
||||
$payload = $message->getPayload() ?? [];
|
||||
$payload = $message->getPayload();
|
||||
|
||||
if (empty($payload)) {
|
||||
throw new \Exception('Missing payload');
|
||||
@@ -206,7 +206,7 @@ class Builds extends Action
|
||||
throw new \Exception('Resource not found');
|
||||
}
|
||||
|
||||
if ($isResourceBlocked($project, $resourceKey === 'functions' ? RESOURCE_TYPE_FUNCTIONS : RESOURCE_TYPE_SITES, $resource->getId())) {
|
||||
if ($isResourceBlocked($project, $resource->getCollection() === 'functions' ? RESOURCE_TYPE_FUNCTIONS : RESOURCE_TYPE_SITES, $resource->getId())) {
|
||||
throw new \Exception('Resource is blocked');
|
||||
}
|
||||
|
||||
@@ -226,10 +226,6 @@ class Builds extends Action
|
||||
|
||||
$spec = Config::getParam('specifications')[$resource->getAttribute('buildSpecification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
|
||||
|
||||
if ($resource->getCollection() === 'functions' && \is_null($runtime)) {
|
||||
throw new \Exception('Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
// Realtime preparation
|
||||
$event = "{$resource->getCollection()}.[{$resourceKey}].deployments.[deploymentId].update";
|
||||
$queueForRealtime
|
||||
@@ -829,7 +825,8 @@ class Builds extends Action
|
||||
|
||||
Console::log('Runtime creation finished');
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
$latestDeployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
if ($latestDeployment->getAttribute('status') === 'canceled') {
|
||||
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
|
||||
|
||||
return;
|
||||
@@ -1259,21 +1256,6 @@ class Builds extends Action
|
||||
*/
|
||||
protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime, ?string $adapter): void
|
||||
{
|
||||
if (! ($queueForRealtime instanceof Realtime)) {
|
||||
throw new Exception('queueForRealtime must be an instance of Realtime');
|
||||
}
|
||||
if (! ($dbForProject instanceof Database)) {
|
||||
throw new Exception('dbForProject must be an instance of Database');
|
||||
}
|
||||
if (! ($deployment instanceof Document)) {
|
||||
throw new Exception('deployment must be an instance of Document');
|
||||
}
|
||||
if (! is_array($runtime)) {
|
||||
throw new Exception('runtime must be an array');
|
||||
}
|
||||
if (! is_string($adapter) && ! is_null($adapter)) {
|
||||
throw new Exception('adapter must be a string or null');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1283,13 +1265,6 @@ class Builds extends Action
|
||||
Document $project,
|
||||
Document $deployment,
|
||||
): void {
|
||||
if (! ($project instanceof Document)) {
|
||||
throw new Exception('project must be an instance of Document');
|
||||
}
|
||||
|
||||
if (! ($deployment instanceof Document)) {
|
||||
throw new Exception('deployment must be an instance of Document');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRuntime(Document $resource, string $version): array
|
||||
@@ -1313,6 +1288,7 @@ class Builds extends Action
|
||||
return match ($resource->getCollection()) {
|
||||
'functions' => $resource->getAttribute('version', 'v2'),
|
||||
'sites' => 'v5',
|
||||
default => throw new \Exception('Unsupported resource type "' . $resource->getCollection() . '".'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1445,11 +1421,10 @@ class Builds extends Action
|
||||
]);
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$previewUrl = match ($resource->getCollection()) {
|
||||
'functions' => '',
|
||||
'sites' => !$rule->isEmpty() ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : '',
|
||||
default => throw new \Exception('Invalid resource type')
|
||||
};
|
||||
$previewUrl = '';
|
||||
if ($resource->getCollection() === 'sites' && !$rule->isEmpty()) {
|
||||
$previewUrl = "{$protocol}://" . $rule->getAttribute('domain', '');
|
||||
}
|
||||
|
||||
$comment = new Comment($platform);
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $commentId));
|
||||
|
||||
@@ -57,7 +57,7 @@ class Screenshots extends Action
|
||||
): void {
|
||||
Console::log('Screenshot action started');
|
||||
|
||||
$payload = $message->getPayload() ?? [];
|
||||
$payload = $message->getPayload();
|
||||
|
||||
if (empty($payload)) {
|
||||
throw new \Exception('Missing payload');
|
||||
@@ -162,7 +162,7 @@ class Screenshots extends Action
|
||||
try {
|
||||
$config = $configs[$key];
|
||||
|
||||
$config['headers'] = \array_merge($config['headers'] ?? [], [
|
||||
$config['headers'] = \array_merge($config['headers'], [
|
||||
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey
|
||||
]);
|
||||
$config['sleep'] = 3000;
|
||||
|
||||
@@ -82,7 +82,7 @@ class Get extends Action
|
||||
}
|
||||
|
||||
$certificatePayload = @openssl_x509_parse($peerCertificate);
|
||||
if ($certificatePayload === false || !\is_array($certificatePayload)) {
|
||||
if ($certificatePayload === false) {
|
||||
throw new Exception(Exception::HEALTH_INVALID_HOST, 'Failed to parse peer certificate for ' . $domain);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use Appwrite\Event\Publisher\Screenshot;
|
||||
use Appwrite\Event\Publisher\StatsResources as StatsResourcesPublisher;
|
||||
use Appwrite\Event\Publisher\Usage as UsagePublisher;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Health\Http\Health\Queue\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
@@ -123,6 +124,7 @@ class Get extends Base
|
||||
System::getEnv('_APP_SCREENSHOTS_QUEUE_NAME', Event::SCREENSHOTS_QUEUE_NAME) => $publisherForScreenshots,
|
||||
System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME) => $queueForMessaging,
|
||||
System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME) => $publisherForMigrations,
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unknown queue name: ' . $name),
|
||||
};
|
||||
$failed = $queue->getSize(failed: true);
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ class Delete extends Action
|
||||
|
||||
$key = $dbForPlatform->getDocument('devKeys', $keyId);
|
||||
|
||||
if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
if ($key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ class Get extends Action
|
||||
|
||||
$key = $dbForPlatform->getDocument('devKeys', $keyId);
|
||||
|
||||
if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
if ($key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class Update extends Action
|
||||
|
||||
$key = $dbForPlatform->getDocument('devKeys', $keyId);
|
||||
|
||||
if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
if ($key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ class XList extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$selectQueries = Query::groupByType($queries)['selections'] ?? [];
|
||||
$selectQueries = Query::groupByType($queries)['selections'];
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$projects = $this->find($dbForPlatform, $queries, $selectQueries);
|
||||
|
||||
@@ -164,9 +164,7 @@ class Action extends PlatformAction
|
||||
$validator = new AnyOf($cnameValidators);
|
||||
$validators[] = $validator;
|
||||
|
||||
if (\is_null($mainValidator)) {
|
||||
$mainValidator = $validator;
|
||||
}
|
||||
$mainValidator = $validator;
|
||||
}
|
||||
|
||||
// Ensure at least one of CNAME/A/AAAA record points to our servers properly
|
||||
|
||||
@@ -84,7 +84,8 @@ class Create extends Action
|
||||
|
||||
$collection = match ($resourceType) {
|
||||
'site' => 'sites',
|
||||
'function' => 'functions'
|
||||
'function' => 'functions',
|
||||
default => throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid resource type: ' . $resourceType),
|
||||
};
|
||||
$resource = $dbForProject->getDocument($collection, $resourceId);
|
||||
if ($resource->isEmpty()) {
|
||||
|
||||
@@ -116,7 +116,7 @@ class XList extends Base
|
||||
|
||||
$grouped = Query::groupByType($queries);
|
||||
$filterQueries = $grouped['filters'];
|
||||
$selectQueries = $grouped['selections'] ?? [];
|
||||
$selectQueries = $grouped['selections'];
|
||||
|
||||
try {
|
||||
$results = $dbForProject->find('deployments', $queries);
|
||||
|
||||
@@ -164,10 +164,6 @@ class Update extends Base
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When connecting to VCS (Version Control System), you need to provide "installationId" and "providerBranch".');
|
||||
}
|
||||
|
||||
if ($site->isEmpty()) {
|
||||
throw new Exception(Exception::SITE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (empty($framework)) {
|
||||
$framework = $site->getAttribute('framework');
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ class Get extends Base
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unsupported period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Sites\Http\Usage;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
@@ -107,6 +108,7 @@ class XList extends Base
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unsupported period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -67,11 +67,7 @@ class Delete extends Base
|
||||
}
|
||||
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable === false || $variable->isEmpty()) {
|
||||
if ($variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ class Get extends Base
|
||||
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if (
|
||||
$variable === false ||
|
||||
$variable->isEmpty() ||
|
||||
$variable->getAttribute('resourceInternalId') !== $site->getSequence() ||
|
||||
$variable->getAttribute('resourceType') !== 'site'
|
||||
@@ -74,10 +73,6 @@ class Get extends Base
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable === false || $variable->isEmpty()) {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class Update extends Base
|
||||
}
|
||||
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site') {
|
||||
if ($variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
@@ -384,14 +384,11 @@ class Create extends Action
|
||||
->setAttribute('chunksUploaded', $chunksUploaded);
|
||||
|
||||
/**
|
||||
* Validate create permission and skip authorization in updateDocument
|
||||
* Without this, the file creation will fail when user doesn't have update permission
|
||||
* Skip authorization in updateDocument.
|
||||
* Without this, the file creation will fail when user doesn't have update permission.
|
||||
* However as with chunk upload even if we are updating, we are essentially creating a file
|
||||
* adding it's new chunk so we validate create permission instead of update
|
||||
* adding it's new chunk so we rely on the create-permission check performed earlier.
|
||||
*/
|
||||
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
|
||||
}
|
||||
|
||||
@@ -431,15 +428,11 @@ class Create extends Action
|
||||
->setAttribute('metadata', $metadata);
|
||||
|
||||
/**
|
||||
* Validate create permission and skip authorization in updateDocument
|
||||
* Without this, the file creation will fail when user doesn't have update permission
|
||||
* Skip authorization in updateDocument.
|
||||
* Without this, the file creation will fail when user doesn't have update permission.
|
||||
* However as with chunk upload even if we are updating, we are essentially creating a file
|
||||
* adding it's new chunk so we validate create permission instead of update
|
||||
* adding it's new chunk so we rely on the create-permission check performed earlier.
|
||||
*/
|
||||
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
try {
|
||||
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
|
||||
} catch (NotFoundException) {
|
||||
@@ -468,8 +461,5 @@ class Create extends Action
|
||||
*/
|
||||
protected function afterCreateSuccess(Document $file)
|
||||
{
|
||||
if (!($file instanceof Document)) {
|
||||
throw new Exception('file must be an instance of document');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ class Get extends Action
|
||||
|
||||
// when file extension is not provided and the mime type is not one of our supported outputs
|
||||
// we fallback to `jpg` output format
|
||||
$output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type;
|
||||
$output = empty($type) ? (array_search($mime, $outputs) ?: 'jpg') : $type;
|
||||
}
|
||||
|
||||
$startTime = \microtime(true);
|
||||
@@ -243,7 +243,7 @@ class Get extends Action
|
||||
|
||||
$image->crop((int) $width, (int) $height, $gravity);
|
||||
|
||||
if (!empty($opacity) || $opacity === 0) {
|
||||
if (!empty($opacity)) {
|
||||
$image->setOpacity($opacity);
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ class Update extends Action
|
||||
}
|
||||
|
||||
if (\is_null($permissions)) {
|
||||
$permissions = $file->getPermissions() ?? [];
|
||||
$permissions = $file->getPermissions();
|
||||
}
|
||||
|
||||
$file->setAttribute('$permissions', $permissions);
|
||||
|
||||
@@ -143,11 +143,12 @@ class XList extends Action
|
||||
});
|
||||
|
||||
foreach ($stats as $stat) {
|
||||
$bucket = $bucketByStatsId[$stat->getId()];
|
||||
|
||||
if ($bucket) {
|
||||
$bucket->setAttribute('totalSize', $stat->getAttribute('value', 0));
|
||||
if (!isset($bucketByStatsId[$stat->getId()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bucket = $bucketByStatsId[$stat->getId()];
|
||||
$bucket->setAttribute('totalSize', $stat->getAttribute('value', 0));
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// Stats may not be available, default to 0
|
||||
|
||||
@@ -109,6 +109,7 @@ class Get extends Action
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\\T00:00:00.000P',
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unsupported period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Storage\Http\Usage;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
@@ -92,6 +93,7 @@ class XList extends Action
|
||||
$format = match ($days['period']) {
|
||||
'1h' => 'Y-m-d\\TH:00:00.000P',
|
||||
'1d' => 'Y-m-d\\T00:00:00.000P',
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unsupported period: ' . $days['period']),
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
|
||||
@@ -104,7 +104,7 @@ class Get extends Action
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$projectInternalId = $project->getSequence();
|
||||
|
||||
@@ -121,11 +121,11 @@ class Get extends Action
|
||||
if (!empty($code)) {
|
||||
$oauth2 = new OAuth2Github(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
|
||||
|
||||
$accessToken = $oauth2->getAccessToken($code) ?? '';
|
||||
$refreshToken = $oauth2->getRefreshToken($code) ?? '';
|
||||
$accessToken = $oauth2->getAccessToken($code);
|
||||
$refreshToken = $oauth2->getRefreshToken($code);
|
||||
$accessTokenExpiry = DateTime::addSeconds(new \DateTime(), \intval($oauth2->getAccessTokenExpiry($code)));
|
||||
|
||||
$personalSlug = $oauth2->getUserSlug($accessToken) ?? '';
|
||||
$personalSlug = $oauth2->getUserSlug($accessToken);
|
||||
$personal = $personalSlug === $owner;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ trait Deployment
|
||||
$activate = true;
|
||||
}
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
} catch (RepositoryNotFound $e) {
|
||||
|
||||
@@ -59,7 +59,7 @@ class Get extends Action
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation === false || $installation->isEmpty()) {
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -73,9 +73,9 @@ class XList extends Action
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
@@ -83,7 +83,7 @@ class XList extends Action
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$branches = $github->listBranches($owner, $repositoryName) ?? [];
|
||||
$branches = $github->listBranches($owner, $repositoryName);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'branches' => \array_map(function ($branch) {
|
||||
|
||||
@@ -79,7 +79,7 @@ class Get extends Action
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ class Create extends Action
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Provider Error: ' . $repository['message']);
|
||||
}
|
||||
|
||||
$repository['id'] = \strval($repository['id']) ?? '';
|
||||
$repository['id'] = \strval($repository['id']);
|
||||
$repository['pushedAt'] = $repository['pushed_at'] ?? '';
|
||||
$repository['organization'] = $installation->getAttribute('organization', '');
|
||||
$repository['provider'] = $installation->getAttribute('provider', '');
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ class Create extends Action
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ class Get extends Action
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId);
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class Get extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$repository['id'] = \strval($repository['id']) ?? '';
|
||||
$repository['id'] = \strval($repository['id']);
|
||||
$repository['pushedAt'] = $repository['pushed_at'] ?? '';
|
||||
$repository['organization'] = $installation->getAttribute('organization', '');
|
||||
$repository['provider'] = $installation->getAttribute('provider', '');
|
||||
|
||||
@@ -109,7 +109,7 @@ class Install extends Action
|
||||
file_put_contents($this->path . '/' . $composeFileName . '.' . $time . '.backup', $data);
|
||||
$compose = new Compose($data);
|
||||
$appwrite = $compose->getService('appwrite');
|
||||
$oldVersion = $appwrite?->getImageVersion();
|
||||
$oldVersion = $appwrite->getImageVersion();
|
||||
try {
|
||||
$ports = $compose->getService('traefik')->getPorts();
|
||||
} catch (\Throwable $th) {
|
||||
@@ -122,10 +122,6 @@ class Install extends Action
|
||||
|
||||
if ($oldVersion) {
|
||||
foreach ($compose->getServices() as $service) {
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$env = $service->getEnvironment()->list();
|
||||
|
||||
foreach ($env as $key => $value) {
|
||||
@@ -177,9 +173,6 @@ class Install extends Action
|
||||
// can be detected by the DB service name or _APP_DB_HOST.
|
||||
$existingDatabase = null;
|
||||
foreach ($compose->getServices() as $service) {
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
$svcEnv = $service->getEnvironment()->list();
|
||||
if (isset($svcEnv['_APP_DB_ADAPTER'])) {
|
||||
$existingDatabase = $svcEnv['_APP_DB_ADAPTER'];
|
||||
@@ -229,8 +222,8 @@ class Install extends Action
|
||||
$assistantExistsInOldCompose = false;
|
||||
if ($existingInstallation) {
|
||||
try {
|
||||
$assistantService = $compose->getService('appwrite-assistant');
|
||||
$assistantExistsInOldCompose = $assistantService !== null;
|
||||
$compose->getService('appwrite-assistant');
|
||||
$assistantExistsInOldCompose = true;
|
||||
} catch (\Throwable) {
|
||||
/* ignore */
|
||||
}
|
||||
@@ -290,7 +283,7 @@ class Install extends Action
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($var['name'] === '_APP_DB_ADAPTER' && $data !== false) {
|
||||
if ($var['name'] === '_APP_DB_ADAPTER' && $data !== '') {
|
||||
$userInput[$var['name']] = $database;
|
||||
continue;
|
||||
}
|
||||
@@ -334,7 +327,7 @@ class Install extends Action
|
||||
|
||||
@unlink(InstallerServer::INSTALLER_COMPLETE_FILE);
|
||||
|
||||
$state = new State([]);
|
||||
$state = new State();
|
||||
$state->clearStaleLock();
|
||||
|
||||
$installerConfig = $this->readInstallerConfig();
|
||||
@@ -608,7 +601,7 @@ class Install extends Action
|
||||
$this->copyMongoEntrypointIfNeeded();
|
||||
}
|
||||
|
||||
if (!$noStart && $startIndex <= 2) {
|
||||
if (!$noStart) {
|
||||
$currentStep = InstallerServer::STEP_DOCKER_CONTAINERS;
|
||||
$this->updateProgress($progress, InstallerServer::STEP_DOCKER_CONTAINERS, InstallerServer::STATUS_IN_PROGRESS, $messages);
|
||||
$this->runDockerCompose($input, $isLocalInstall, $useExistingConfig, $isCLI, $progress, $isUpgrade);
|
||||
@@ -838,7 +831,7 @@ class Install extends Action
|
||||
'email' => $email,
|
||||
'domain' => $domain,
|
||||
'database' => $database,
|
||||
'ip' => ($hostIp !== false && $hostIp !== $domain) ? $hostIp : null,
|
||||
'ip' => ($hostIp !== $domain) ? $hostIp : null,
|
||||
'os' => php_uname('s') . ' ' . php_uname('r'),
|
||||
'arch' => php_uname('m'),
|
||||
'cpus' => ((int) trim((string) \shell_exec('nproc'))) ?: null,
|
||||
@@ -1365,9 +1358,6 @@ class Install extends Action
|
||||
}
|
||||
|
||||
foreach ($compose->getServices() as $service) {
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
$env = $service->getEnvironment()->list();
|
||||
$host = $env['_APP_DB_HOST'] ?? null;
|
||||
if ($host !== null && in_array($host, $dbServices, true)) {
|
||||
|
||||
@@ -75,7 +75,6 @@ class Interval extends Action
|
||||
protected function getTasks(): array
|
||||
{
|
||||
$intervalDomainVerification = (int) System::getEnv('_APP_INTERVAL_DOMAIN_VERIFICATION', '120'); // 2 minutes
|
||||
$intervalCleanupStaleExecutions = (int) System::getEnv('_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS', '300'); // 5 minutes
|
||||
|
||||
return [
|
||||
[
|
||||
@@ -135,50 +134,4 @@ class Interval extends Action
|
||||
Span::add("interval.domainVerification.processed", $processed);
|
||||
Span::add("interval.domainVerification.failed", $failed);
|
||||
}
|
||||
|
||||
private function cleanupStaleExecutions(Database $dbForPlatform, callable $getProjectDB): void
|
||||
{
|
||||
$staleThreshold = DatabaseDateTime::addSeconds(new DateTime(), -1200); // 20 minutes ago
|
||||
|
||||
$scanned = 0;
|
||||
$processed = 0;
|
||||
$failed = 0;
|
||||
|
||||
$dbForPlatform->foreach(
|
||||
'projects',
|
||||
function (Document $project) use ($getProjectDB, $staleThreshold, &$scanned, &$processed, &$failed) {
|
||||
try {
|
||||
$dbForProject = $getProjectDB($project);
|
||||
|
||||
$staleExecutions = $dbForProject->find('executions', [
|
||||
Query::equal('status', ['processing']),
|
||||
Query::lessThan('$createdAt', $staleThreshold),
|
||||
Query::limit(100),
|
||||
]);
|
||||
|
||||
$scanned += \count($staleExecutions);
|
||||
|
||||
if (\count($staleExecutions) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($staleExecutions as $execution) {
|
||||
$dbForProject->updateDocument('executions', $execution->getId(), new Document(['status' => 'failed', 'errors' => 'Execution timed out']));
|
||||
}
|
||||
|
||||
$processed++;
|
||||
} catch (\Throwable $th) {
|
||||
$failed++;
|
||||
}
|
||||
},
|
||||
[
|
||||
Query::equal('region', [System::getEnv('_APP_REGION', 'default')]),
|
||||
Query::limit(100),
|
||||
]
|
||||
);
|
||||
|
||||
Span::add("interval.cleanupStaleExecutions.scanned", $scanned);
|
||||
Span::add("interval.cleanupStaleExecutions.processed", $processed);
|
||||
Span::add("interval.cleanupStaleExecutions.failed", $failed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ class SDKs extends Action
|
||||
|
||||
Console::log('');
|
||||
|
||||
if ($createRelease && ! $examplesOnly) {
|
||||
if ($createRelease) {
|
||||
Console::info("━━━ {$language['name']} SDK ({$platform['name']}, {$language['version']}) ━━━");
|
||||
$changelog = $language['changelog'] ?? '';
|
||||
$changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log';
|
||||
@@ -1146,7 +1146,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
if (! empty($prListOutput[0])) {
|
||||
$parts = \explode(' ', trim($prListOutput[0]), 2);
|
||||
$prNumber = $parts[0] ?? '';
|
||||
$prNumber = $parts[0];
|
||||
$prUrl = $parts[1] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ abstract class ScheduleBase extends Action
|
||||
$this->collectSchedules($dbForPlatform, $getProjectDB, $lastSyncUpdate, $isResourceBlocked);
|
||||
});
|
||||
|
||||
while (true) {
|
||||
for (;;) {
|
||||
try {
|
||||
go(fn () => $this->enqueueResources($dbForPlatform, $getProjectDB));
|
||||
$this->scheduleTelemetryCount->record(count($this->schedules), ['resourceType' => static::getSupportedResource()]);
|
||||
|
||||
@@ -21,8 +21,6 @@ class ScheduleFunctions extends ScheduleBase
|
||||
public const UPDATE_TIMER = 10; // seconds
|
||||
public const ENQUEUE_TIMER = 60; // seconds
|
||||
|
||||
private ?float $lastEnqueueUpdate = null;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'schedule-functions';
|
||||
@@ -43,7 +41,10 @@ class ScheduleFunctions extends ScheduleBase
|
||||
$timerStart = \microtime(true);
|
||||
$time = DateTime::now();
|
||||
|
||||
$enqueueDiff = $this->lastEnqueueUpdate === null ? 0 : $timerStart - $this->lastEnqueueUpdate;
|
||||
// TODO: Track the last enqueue timestamp to subtract ENQUEUE_TIMER drift from
|
||||
// the time frame. Previously this used $this->lastEnqueueUpdate as a property
|
||||
// but enabling the assignment broke scheduling, so the diff stays 0.
|
||||
$enqueueDiff = 0;
|
||||
$timeFrame = DateTime::addSeconds(new \DateTime(), static::ENQUEUE_TIMER - $enqueueDiff);
|
||||
|
||||
Console::log("Enqueue tick: started at: $time (with diff $enqueueDiff)");
|
||||
@@ -128,9 +129,6 @@ class ScheduleFunctions extends ScheduleBase
|
||||
|
||||
$timerEnd = \microtime(true);
|
||||
|
||||
// TODO: This was a bug before because it wasn't passed by reference, enabling it breaks scheduling
|
||||
//$this->lastEnqueueUpdate = $timerStart;
|
||||
|
||||
Console::log("Enqueue tick: {$total} executions were enqueued in " . ($timerEnd - $timerStart) . " seconds");
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user