Merge branch '1.8.x' into ser-539

This commit is contained in:
Hemachandar
2025-11-26 11:10:52 +05:30
254 changed files with 2297 additions and 1466 deletions
+1 -1
View File
@@ -215,7 +215,7 @@ return [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'outputDirectory' => './.output',
'startCommand' => 'bash helpers/tanstack-start/server.sh',
],
'static' => [
+1 -1
View File
@@ -226,7 +226,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '11.1.1',
'version' => '12.0.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
-939
View File
@@ -2,9 +2,7 @@
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Auth\MFA\Challenge;
use Appwrite\Auth\MFA\Type;
use Appwrite\Auth\MFA\Type\TOTP;
use Appwrite\Auth\OAuth2\Exception as OAuth2Exception;
use Appwrite\Auth\Phrase;
use Appwrite\Auth\Validator\Password;
@@ -4153,943 +4151,6 @@ App::put('/v1/account/verifications/phone')
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
});
App::patch('/v1/account/mfa')
->desc('Update MFA')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFA',
description: '/docs/references/account/update-mfa.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_USER,
)
],
contentType: ContentType::JSON
))
->param('mfa', null, new Boolean(), 'Enable or disable MFA.')
->inject('requestTimestamp')
->inject('response')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (bool $mfa, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$user->setAttribute('mfa', $mfa);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
if ($mfa) {
$factors = $session->getAttribute('factors', []);
$totp = TOTP::getAuthenticatorFromUser($user);
if ($totp !== null && $totp->getAttribute('verified', false)) {
$factors[] = Type::TOTP;
}
if ($user->getAttribute('email', false) && $user->getAttribute('emailVerification', false)) {
$factors[] = Type::EMAIL;
}
if ($user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)) {
$factors[] = Type::PHONE;
}
$factors = \array_values(\array_unique($factors));
$session->setAttribute('factors', $factors);
$dbForProject->updateDocument('sessions', $session->getId(), $session);
}
$queueForEvents->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
App::get('/v1/account/mfa/factors')
->desc('List factors')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/account/list-mfa-factors.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_FACTORS,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.listMFAFactors',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'listMFAFactors',
description: '/docs/references/account/list-mfa-factors.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_FACTORS,
)
],
contentType: ContentType::JSON
)
])
->inject('response')
->inject('user')
->action(function (Response $response, Document $user) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
$recoveryCodeEnabled = \is_array($mfaRecoveryCodes) && \count($mfaRecoveryCodes) > 0;
$totp = TOTP::getAuthenticatorFromUser($user);
$factors = new Document([
Type::TOTP => $totp !== null && $totp->getAttribute('verified', false),
Type::EMAIL => $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false),
Type::PHONE => $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false),
Type::RECOVERY_CODE => $recoveryCodeEnabled
]);
$response->dynamic($factors, Response::MODEL_MFA_FACTORS);
});
App::post('/v1/account/mfa/authenticators/:type')
->desc('Create authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaAuthenticator',
description: '/docs/references/account/create-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_TYPE,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.createMFAAuthenticator',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'createMFAAuthenticator',
description: '/docs/references/account/create-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_TYPE,
)
],
contentType: ContentType::JSON
)
])
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator. Must be `' . Type::TOTP . '`')
->inject('requestTimestamp')
->inject('response')
->inject('project')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $type, ?\DateTime $requestTimestamp, Response $response, Document $project, Document $user, Database $dbForProject, Event $queueForEvents) {
$otp = (match ($type) {
Type::TOTP => new TOTP(),
default => throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Unknown type.') // Ideally never happens if param validator stays always in sync
});
$otp->setLabel($user->getAttribute('email'));
$otp->setIssuer($project->getAttribute('name'));
$authenticator = TOTP::getAuthenticatorFromUser($user);
if ($authenticator) {
if ($authenticator->getAttribute('verified')) {
throw new Exception(Exception::USER_AUTHENTICATOR_ALREADY_VERIFIED);
}
$dbForProject->deleteDocument('authenticators', $authenticator->getId());
}
$authenticator = new Document([
'$id' => ID::unique(),
'userId' => $user->getId(),
'userInternalId' => $user->getSequence(),
'type' => Type::TOTP,
'verified' => false,
'data' => [
'secret' => $otp->getSecret(),
],
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
]
]);
$model = new Document([
'secret' => $otp->getSecret(),
'uri' => $otp->getProvisioningUri()
]);
$authenticator = $dbForProject->createDocument('authenticators', $authenticator);
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents->setParam('userId', $user->getId());
$response->dynamic($model, Response::MODEL_MFA_TYPE);
});
App::put('/v1/account/mfa/authenticators/:type')
->desc('Update authenticator (confirmation)')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaAuthenticator',
description: '/docs/references/account/update-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_USER,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.updateMFAAuthenticator',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFAAuthenticator',
description: '/docs/references/account/update-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_USER,
)
],
contentType: ContentType::JSON
)
])
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $type, string $otp, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$authenticator = (match ($type) {
Type::TOTP => TOTP::getAuthenticatorFromUser($user),
default => null
});
if ($authenticator === null) {
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
if ($authenticator->getAttribute('verified')) {
throw new Exception(Exception::USER_AUTHENTICATOR_ALREADY_VERIFIED);
}
$success = (match ($type) {
Type::TOTP => Challenge\TOTP::verify($user, $otp),
default => false
});
if (!$success) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$authenticator->setAttribute('verified', true);
$dbForProject->updateDocument('authenticators', $authenticator->getId(), $authenticator);
$dbForProject->purgeCachedDocument('users', $user->getId());
$factors = $session->getAttribute('factors', []);
$factors[] = $type;
$factors = \array_values(\array_unique($factors));
$session->setAttribute('factors', $factors);
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
App::post('/v1/account/mfa/recovery-codes')
->desc('Create MFA recovery codes')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/account/create-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_MFA_RECOVERY_CODES,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.createMFARecoveryCodes',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'createMFARecoveryCodes',
description: '/docs/references/account/create-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_MFA_RECOVERY_CODES,
)
],
contentType: ContentType::JSON
)
])
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
if (!empty($mfaRecoveryCodes)) {
throw new Exception(Exception::USER_RECOVERY_CODES_ALREADY_EXISTS);
}
$mfaRecoveryCodes = Type::generateBackupCodes();
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
$dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents->setParam('userId', $user->getId());
$document = new Document([
'recoveryCodes' => $mfaRecoveryCodes
]);
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
});
App::patch('/v1/account/mfa/recovery-codes')
->desc('Update MFA recovery codes (regenerate)')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/account/update-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_RECOVERY_CODES,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.updateMFARecoveryCodes',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFARecoveryCodes',
description: '/docs/references/account/update-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_RECOVERY_CODES,
)
],
contentType: ContentType::JSON
)
])
->inject('dbForProject')
->inject('response')
->inject('user')
->inject('queueForEvents')
->action(function (Database $dbForProject, Response $response, Document $user, Event $queueForEvents) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
if (empty($mfaRecoveryCodes)) {
throw new Exception(Exception::USER_RECOVERY_CODES_NOT_FOUND);
}
$mfaRecoveryCodes = Type::generateBackupCodes();
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
$dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents->setParam('userId', $user->getId());
$document = new Document([
'recoveryCodes' => $mfaRecoveryCodes
]);
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
});
App::get('/v1/account/mfa/recovery-codes')
->desc('List MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('scope', 'account')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/account/get-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_RECOVERY_CODES,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.getMFARecoveryCodes',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'getMFARecoveryCodes',
description: '/docs/references/account/get-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_MFA_RECOVERY_CODES,
)
],
contentType: ContentType::JSON
)
])
->inject('response')
->inject('user')
->action(function (Response $response, Document $user) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
if (empty($mfaRecoveryCodes)) {
throw new Exception(Exception::USER_RECOVERY_CODES_NOT_FOUND);
}
$document = new Document([
'recoveryCodes' => $mfaRecoveryCodes
]);
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
});
App::delete('/v1/account/mfa/authenticators/:type')
->desc('Delete authenticator')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].delete.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/account/delete-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::NONE,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.deleteMFAAuthenticator',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'deleteMFAAuthenticator',
description: '/docs/references/account/delete-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::NONE
)
])
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $type, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$authenticator = (match ($type) {
Type::TOTP => TOTP::getAuthenticatorFromUser($user),
default => null
});
if (!$authenticator) {
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
$dbForProject->deleteDocument('authenticators', $authenticator->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents->setParam('userId', $user->getId());
$response->noContent();
});
App::post('/v1/account/mfa/challenge')
->desc('Create MFA challenge')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].challenges.[challengeId].create')
->label('audits.event', 'challenge.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaChallenge',
description: '/docs/references/account/create-mfa-challenge.md',
auth: [],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_MFA_CHALLENGE,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.createMFAChallenge',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'createMFAChallenge',
description: '/docs/references/account/create-mfa-challenge.md',
auth: [],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_MFA_CHALLENGE,
)
],
contentType: ContentType::JSON
)
])
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{userId}')
->param('factor', '', new WhiteList([Type::EMAIL, Type::PHONE, Type::TOTP, Type::RECOVERY_CODE]), 'Factor used for verification. Must be one of following: `' . Type::EMAIL . '`, `' . Type::PHONE . '`, `' . Type::TOTP . '`, `' . Type::RECOVERY_CODE . '`.')
->inject('response')
->inject('dbForProject')
->inject('user')
->inject('locale')
->inject('project')
->inject('request')
->inject('queueForEvents')
->inject('queueForMessaging')
->inject('queueForMails')
->inject('timelimit')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
$code = Auth::codeGenerator();
$challenge = new Document([
'userId' => $user->getId(),
'userInternalId' => $user->getSequence(),
'type' => $factor,
'token' => Auth::tokenGenerator(),
'code' => $code,
'expire' => $expire,
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
]);
$challenge = $dbForProject->createDocument('challenges', $challenge);
switch ($factor) {
case Type::PHONE:
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
if (empty($user->getAttribute('phone'))) {
throw new Exception(Exception::USER_PHONE_NOT_FOUND);
}
if (!$user->getAttribute('phoneVerification')) {
throw new Exception(Exception::USER_PHONE_NOT_VERIFIED);
}
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.mfaChallenge-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $code);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$phone = $user->getAttribute('phone');
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage(new Document([
'$id' => $challenge->getId(),
'data' => [
'content' => $code,
],
]))
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
if (isset($plan['authPhone'])) {
$timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days
$timelimit
->setParam('{organizationId}', $project->getAttribute('teamId'));
$abuse = new Abuse($timelimit);
if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
$helper = PhoneNumberUtil::getInstance();
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForStatsUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForStatsUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
break;
case Type::EMAIL:
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
if (empty($user->getAttribute('email'))) {
throw new Exception(Exception::USER_EMAIL_NOT_FOUND);
}
if (!$user->getAttribute('emailVerification')) {
throw new Exception(Exception::USER_EMAIL_NOT_VERIFIED);
}
$subject = $locale->getText("emails.mfaChallenge.subject");
$preview = $locale->getText("emails.mfaChallenge.preview");
$heading = $locale->getText("emails.mfaChallenge.heading");
$customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? [];
$smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base');
$validator = new FileName();
if (!$validator->isValid($smtpBaseTemplate)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path');
}
$bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl';
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$agentOs = $detector->getOS();
$agentClient = $detector->getClient();
$agentDevice = $detector->getDevice();
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-mfa-challenge.tpl');
$message
->setParam('{{hello}}', $locale->getText("emails.mfaChallenge.hello"))
->setParam('{{description}}', $locale->getText("emails.mfaChallenge.description"))
->setParam('{{clientInfo}}', $locale->getText("emails.mfaChallenge.clientInfo"))
->setParam('{{thanks}}', $locale->getText("emails.mfaChallenge.thanks"))
->setParam('{{signature}}', $locale->getText("emails.mfaChallenge.signature"));
$body = $message->render();
$smtp = $project->getAttribute('smtp', []);
$smtpEnabled = $smtp['enabled'] ?? false;
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
$senderEmail = $smtp['senderEmail'];
}
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
}
$queueForMails
->setSmtpHost($smtp['host'] ?? '')
->setSmtpPort($smtp['port'] ?? '')
->setSmtpUsername($smtp['username'] ?? '')
->setSmtpPassword($smtp['password'] ?? '')
->setSmtpSecure($smtp['secure'] ?? '');
if (!empty($customTemplate)) {
if (!empty($customTemplate['senderEmail'])) {
$senderEmail = $customTemplate['senderEmail'];
}
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
}
$body = $customTemplate['message'] ?? '';
$subject = $customTemplate['subject'] ?? $subject;
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
$emailVariables = [
'heading' => $heading,
'direction' => $locale->getText('settings.direction'),
// {{user}}, {{project}} and {{otp}} are required in the templates
'user' => $user->getAttribute('name'),
'project' => $project->getAttribute('name'),
'otp' => $code,
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
'agentOs' => $agentOs['osName'] ?? 'UNKNOWN',
];
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
$emailVariables = array_merge($emailVariables, [
'accentColor' => APP_EMAIL_ACCENT_COLOR,
'logoUrl' => APP_EMAIL_LOGO_URL,
'twitterUrl' => APP_SOCIAL_TWITTER,
'discordUrl' => APP_SOCIAL_DISCORD,
'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE,
'termsUrl' => APP_EMAIL_TERMS_URL,
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
]);
}
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setBodyTemplate($bodyTemplate)
->setVariables($emailVariables)
->setRecipient($user->getAttribute('email'))
->trigger();
break;
}
$queueForEvents
->setParam('userId', $user->getId())
->setParam('challengeId', $challenge->getId());
$response->dynamic($challenge, Response::MODEL_MFA_CHALLENGE);
});
App::put('/v1/account/mfa/challenge')
->desc('Update MFA challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('audits.event', 'challenges.update')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('sdk', [
new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaChallenge',
description: '/docs/references/account/update-mfa-challenge.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SESSION,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.updateMFAChallenge',
),
),
new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFAChallenge',
description: '/docs/references/account/update-mfa-challenge.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SESSION,
)
],
contentType: ContentType::JSON
)
])
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},challengeId:{param-challengeId}')
->param('challengeId', '', new Text(256), 'ID of the challenge.')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('project')
->inject('response')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $challengeId, string $otp, Document $project, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$challenge = $dbForProject->getDocument('challenges', $challengeId);
if ($challenge->isEmpty()) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$type = $challenge->getAttribute('type');
$recoveryCodeChallenge = function (Document $challenge, Document $user, string $otp) use ($dbForProject) {
if (
$challenge->isSet('type') &&
$challenge->getAttribute('type') === \strtolower(Type::RECOVERY_CODE)
) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
if (in_array($otp, $mfaRecoveryCodes)) {
$mfaRecoveryCodes = array_diff($mfaRecoveryCodes, [$otp]);
$mfaRecoveryCodes = array_values($mfaRecoveryCodes);
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
$dbForProject->updateDocument('users', $user->getId(), $user);
return true;
}
return false;
}
return false;
};
$success = (match ($type) {
Type::TOTP => Challenge\TOTP::challenge($challenge, $user, $otp),
Type::PHONE => Challenge\Phone::challenge($challenge, $user, $otp),
Type::EMAIL => Challenge\Email::challenge($challenge, $user, $otp),
\strtolower(Type::RECOVERY_CODE) => $recoveryCodeChallenge($challenge, $user, $otp),
default => false
});
if (!$success) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$dbForProject->deleteDocument('challenges', $challengeId);
$dbForProject->purgeCachedDocument('users', $user->getId());
$factors = $session->getAttribute('factors', []);
$factors[] = $type;
$factors = \array_values(\array_unique($factors));
$session
->setAttribute('factors', $factors)
->setAttribute('mfaUpdatedAt', DateTime::now());
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId());
$response->dynamic($session, Response::MODEL_SESSION);
});
App::post('/v1/account/targets/push')
->desc('Create push target')
->groups(['api', 'account'])
+2 -1
View File
@@ -1509,6 +1509,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
}
$isInternal = $decoded['internal'] ?? false;
$disposition = $decoded['disposition'] ?? 'inline';
$dbForProject = $isInternal ? $dbForPlatform : $dbForProject;
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -1565,7 +1566,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Content-Disposition', $disposition . '; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage());
+3
View File
@@ -5,6 +5,7 @@ use Appwrite\Event\Build;
use Appwrite\Event\Delete;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Redirect;
use Appwrite\Platform\Modules\Compute\Base as ComputeBase;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
@@ -433,6 +434,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
}
ComputeBase::updateEmptyManualRule($project, $resource, $deployment, $dbForPlatform);
if ($resource->getCollection() === 'sites' && !empty($latestCommentId) && !empty($previewRuleId)) {
$retries = 0;
$lockAcquired = false;
+1 -1
View File
@@ -859,7 +859,7 @@ $image = $this->getParam('image', '');
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-browser:
image: appwrite/browser:0.3.1
image: appwrite/browser:0.3.2
container_name: appwrite-browser
<<: *x-logging
restart: unless-stopped
+1 -1
View File
@@ -63,7 +63,7 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.20.*",
"utopia-php/migration": "1.*",
"utopia-php/migration": "1.3.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
Generated
+184 -75
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "0180f566c380723a1d0d35b44f346abf",
"content-hash": "de089be13fb56313c3554a681070b2a8",
"packages": [
{
"name": "adhocore/jwt",
@@ -283,16 +283,16 @@
},
{
"name": "brick/math",
"version": "0.14.0",
"version": "0.14.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2"
"reference": "f05858549e5f9d7bb45875a75583240a38a281d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
"reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
"url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0",
"reference": "f05858549e5f9d7bb45875a75583240a38a281d0",
"shasum": ""
},
"require": {
@@ -331,7 +331,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.14.0"
"source": "https://github.com/brick/math/tree/0.14.1"
},
"funding": [
{
@@ -339,7 +339,7 @@
"type": "github"
}
],
"time": "2025-08-29T12:40:03+00:00"
"time": "2025-11-24T14:40:29+00:00"
},
{
"name": "chillerlan/php-qrcode",
@@ -1236,16 +1236,16 @@
},
{
"name": "open-telemetry/api",
"version": "1.7.0",
"version": "1.7.1",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/api.git",
"reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522"
"reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522",
"reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4",
"reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4",
"shasum": ""
},
"require": {
@@ -1265,7 +1265,7 @@
]
},
"branch-alias": {
"dev-main": "1.7.x-dev"
"dev-main": "1.8.x-dev"
}
},
"autoload": {
@@ -1298,11 +1298,11 @@
],
"support": {
"chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
"docs": "https://opentelemetry.io/docs/php",
"docs": "https://opentelemetry.io/docs/languages/php",
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-10-02T23:44:28+00:00"
"time": "2025-10-19T10:49:48+00:00"
},
{
"name": "open-telemetry/context",
@@ -1365,16 +1365,16 @@
},
{
"name": "open-telemetry/exporter-otlp",
"version": "1.3.2",
"version": "1.3.3",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/exporter-otlp.git",
"reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2"
"reference": "07b02bc71838463f6edcc78d3485c04b48fb263d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/196f3a1dbce3b2c0f8110d164232c11ac00ddbb2",
"reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/07b02bc71838463f6edcc78d3485c04b48fb263d",
"reference": "07b02bc71838463f6edcc78d3485c04b48fb263d",
"shasum": ""
},
"require": {
@@ -1421,11 +1421,11 @@
],
"support": {
"chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
"docs": "https://opentelemetry.io/docs/php",
"docs": "https://opentelemetry.io/docs/languages/php",
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-06-16T00:24:51+00:00"
"time": "2025-11-13T08:04:37+00:00"
},
{
"name": "open-telemetry/gen-otlp-protobuf",
@@ -1492,16 +1492,16 @@
},
{
"name": "open-telemetry/sdk",
"version": "1.9.0",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sdk.git",
"reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e"
"reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e",
"reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99",
"reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99",
"shasum": ""
},
"require": {
@@ -1581,11 +1581,11 @@
],
"support": {
"chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
"docs": "https://opentelemetry.io/docs/php",
"docs": "https://opentelemetry.io/docs/languages/php",
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-10-02T23:44:28+00:00"
"time": "2025-11-25T10:59:15+00:00"
},
{
"name": "open-telemetry/sem-conv",
@@ -1763,16 +1763,16 @@
},
{
"name": "php-amqplib/php-amqplib",
"version": "v3.7.3",
"version": "v3.7.4",
"source": {
"type": "git",
"url": "https://github.com/php-amqplib/php-amqplib.git",
"reference": "9f50fe69a9f1a19e2cb25596a354d705de36fe59"
"reference": "381b6f7c600e0e0c7463cdd7f7a1a3bc6268e5fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/9f50fe69a9f1a19e2cb25596a354d705de36fe59",
"reference": "9f50fe69a9f1a19e2cb25596a354d705de36fe59",
"url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/381b6f7c600e0e0c7463cdd7f7a1a3bc6268e5fd",
"reference": "381b6f7c600e0e0c7463cdd7f7a1a3bc6268e5fd",
"shasum": ""
},
"require": {
@@ -1838,9 +1838,9 @@
],
"support": {
"issues": "https://github.com/php-amqplib/php-amqplib/issues",
"source": "https://github.com/php-amqplib/php-amqplib/tree/v3.7.3"
"source": "https://github.com/php-amqplib/php-amqplib/tree/v3.7.4"
},
"time": "2025-02-18T20:11:13+00:00"
"time": "2025-11-23T17:00:56+00:00"
},
{
"name": "php-http/discovery",
@@ -3844,12 +3844,12 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "ad744de0844a61bc232b24544ac49d4168251f91"
"reference": "b00fcf1103164e0ace57e4e2fac07bcc9431b65e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/ad744de0844a61bc232b24544ac49d4168251f91",
"reference": "ad744de0844a61bc232b24544ac49d4168251f91",
"url": "https://api.github.com/repos/utopia-php/database/zipball/b00fcf1103164e0ace57e4e2fac07bcc9431b65e",
"reference": "b00fcf1103164e0ace57e4e2fac07bcc9431b65e",
"shasum": ""
},
"require": {
@@ -3925,20 +3925,20 @@
"source": "https://github.com/utopia-php/database/tree/ser-541-3.x",
"issues": "https://github.com/utopia-php/database/issues"
},
"time": "2025-11-13T12:53:38+00:00"
"time": "2025-11-26T05:35:31+00:00"
},
{
"name": "utopia-php/detector",
"version": "0.2.2",
"version": "0.2.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/detector.git",
"reference": "9a41be5f21efe2d865de79b08dff94fff85ce5e9"
"reference": "c1f49b3e82250c3256ffba48aa9737d6c17a243a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/detector/zipball/9a41be5f21efe2d865de79b08dff94fff85ce5e9",
"reference": "9a41be5f21efe2d865de79b08dff94fff85ce5e9",
"url": "https://api.github.com/repos/utopia-php/detector/zipball/c1f49b3e82250c3256ffba48aa9737d6c17a243a",
"reference": "c1f49b3e82250c3256ffba48aa9737d6c17a243a",
"shasum": ""
},
"require": {
@@ -3968,9 +3968,57 @@
],
"support": {
"issues": "https://github.com/utopia-php/detector/issues",
"source": "https://github.com/utopia-php/detector/tree/0.2.2"
"source": "https://github.com/utopia-php/detector/tree/0.2.3"
},
"time": "2025-10-31T12:43:31+00:00"
"time": "2025-11-24T15:52:51+00:00"
},
{
"name": "utopia-php/di",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/di.git",
"reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31",
"reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"require-dev": {
"laravel/pint": "^1.2",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5.25",
"swoole/ide-helper": "4.8.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\": "src/",
"Tests\\E2E\\": "tests/e2e"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A simple and lite library for managing dependency injections",
"keywords": [
"framework",
"http",
"php",
"upf"
],
"support": {
"issues": "https://github.com/utopia-php/di/issues",
"source": "https://github.com/utopia-php/di/tree/0.1.0"
},
"time": "2024-08-08T14:35:19+00:00"
},
{
"name": "utopia-php/dns",
@@ -4239,28 +4287,33 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.30",
"version": "0.33.31",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "07cf699a7c47bd1a03b4da1812f1719a66b3c924"
"reference": "cb01940ffe620b3d09f934dc2b8c72c2406309e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/07cf699a7c47bd1a03b4da1812f1719a66b3c924",
"reference": "07cf699a7c47bd1a03b4da1812f1719a66b3c924",
"url": "https://api.github.com/repos/utopia-php/http/zipball/cb01940ffe620b3d09f934dc2b8c72c2406309e7",
"reference": "cb01940ffe620b3d09f934dc2b8c72c2406309e7",
"shasum": ""
},
"require": {
"ext-swoole": "*",
"php": ">=8.1",
"utopia-php/compression": "0.1.*",
"utopia-php/telemetry": "0.1.*"
"utopia-php/servers": "0.2.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/validators": "0.0.*"
},
"require-dev": {
"ext-xdebug": "*",
"laravel/pint": "^1.2",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5.25"
"phpstan/phpstan": "1.*",
"phpunit/phpunit": "^9.5.25",
"swoole/ide-helper": "4.8.3"
},
"type": "library",
"autoload": {
@@ -4272,17 +4325,18 @@
"license": [
"MIT"
],
"description": "A simple, light and advanced PHP framework",
"description": "A simple, light and advanced PHP HTTP framework",
"keywords": [
"framework",
"http",
"php",
"upf"
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.30"
"source": "https://github.com/utopia-php/http/tree/1.2.0"
},
"time": "2025-11-18T12:18:00+00:00"
"time": "2025-10-21T10:53:55+00:00"
},
{
"name": "utopia-php/image",
@@ -4487,16 +4541,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.4.0",
"version": "1.3.6",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "18bd7d39dcee09280f40edb12879c727ecec98d3"
"reference": "4abe70cc242bbffebfa377e4126ee2a4a1c9ef7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/18bd7d39dcee09280f40edb12879c727ecec98d3",
"reference": "18bd7d39dcee09280f40edb12879c727ecec98d3",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/4abe70cc242bbffebfa377e4126ee2a4a1c9ef7e",
"reference": "4abe70cc242bbffebfa377e4126ee2a4a1c9ef7e",
"shasum": ""
},
"require": {
@@ -4536,9 +4590,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.4.0"
"source": "https://github.com/utopia-php/migration/tree/1.3.6"
},
"time": "2025-11-21T06:08:59+00:00"
"time": "2025-11-25T11:36:57+00:00"
},
{
"name": "utopia-php/mongo",
@@ -4924,6 +4978,60 @@
},
"time": "2021-03-10T10:45:22+00:00"
},
{
"name": "utopia-php/servers",
"version": "0.2.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/servers.git",
"reference": "c6ec23b0597f657020a75d4a2f8ef03177d7edaa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/servers/zipball/c6ec23b0597f657020a75d4a2f8ef03177d7edaa",
"reference": "c6ec23b0597f657020a75d4a2f8ef03177d7edaa",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/di": "0.1.*",
"utopia-php/validators": "0.0.*"
},
"require-dev": {
"laravel/pint": "^0.2.3",
"phpstan/phpstan": "^1.8",
"phpunit/phpunit": "^9.5.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Servers\\": "src/Servers"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Team Appwrite",
"email": "team@appwrite.io"
}
],
"description": "A base library for building Utopia style servers.",
"keywords": [
"framework",
"php",
"servers",
"upf",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/servers/issues",
"source": "https://github.com/utopia-php/servers/tree/0.2.1"
},
"time": "2025-10-21T10:08:19+00:00"
},
{
"name": "utopia-php/storage",
"version": "0.18.14",
@@ -5410,16 +5518,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.5.8",
"version": "1.5.9",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "05367bc4a4c3e020e9aca114ae875b626ce8fc55"
"reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/05367bc4a4c3e020e9aca114ae875b626ce8fc55",
"reference": "05367bc4a4c3e020e9aca114ae875b626ce8fc55",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ee434aa00a9185380b9a39bb46bf86d7104d3a93",
"reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93",
"shasum": ""
},
"require": {
@@ -5455,9 +5563,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.8"
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.9"
},
"time": "2025-11-20T11:00:34+00:00"
"time": "2025-11-25T05:22:25+00:00"
},
{
"name": "doctrine/annotations",
@@ -5685,16 +5793,16 @@
},
{
"name": "laravel/pint",
"version": "v1.25.1",
"version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9"
"reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9",
"reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9",
"url": "https://api.github.com/repos/laravel/pint/zipball/69dcca060ecb15e4b564af63d1f642c81a241d6f",
"reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f",
"shasum": ""
},
"require": {
@@ -5705,13 +5813,13 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.87.2",
"illuminate/view": "^11.46.0",
"larastan/larastan": "^3.7.1",
"laravel-zero/framework": "^11.45.0",
"friendsofphp/php-cs-fixer": "^3.90.0",
"illuminate/view": "^12.40.1",
"larastan/larastan": "^3.8.0",
"laravel-zero/framework": "^12.0.4",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3.1",
"pestphp/pest": "^2.36.0"
"nunomaduro/termwind": "^2.3.3",
"pestphp/pest": "^3.8.4"
},
"bin": [
"builds/pint"
@@ -5737,6 +5845,7 @@
"description": "An opinionated code formatter for PHP.",
"homepage": "https://laravel.com",
"keywords": [
"dev",
"format",
"formatter",
"lint",
@@ -5747,7 +5856,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-09-19T02:57:12+00:00"
"time": "2025-11-25T21:15:52+00:00"
},
{
"name": "matthiasmullie/minify",
+1 -1
View File
@@ -958,7 +958,7 @@ services:
appwrite-browser:
container_name: appwrite-browser
image: appwrite/browser:0.3.1
image: appwrite/browser:0.3.2
networks:
- appwrite
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.5.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: &lt;REGION&gt;.cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.5.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.5.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: &lt;REGION&gt;.cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.5.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: &lt;REGION&gt;.cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.5.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.5.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: &lt;REGION&gt;.cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.7.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.7.0
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.7.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.7.0
@@ -13,7 +13,7 @@ account.createOAuth2Session(
OAuthProvider.AMAZON, // provider
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
listOf(), // scopes (optional)
List.of(), // scopes (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -13,7 +13,7 @@ account.createOAuth2Token(
OAuthProvider.AMAZON, // provider
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
listOf(), // scopes (optional)
List.of(), // scopes (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -9,7 +9,7 @@ Client client = new Client(context)
Account account = new Account(client);
account.listIdentities(
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -9,7 +9,7 @@ Client client = new Client(context)
Account account = new Account(client);
account.listLogs(
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -9,10 +9,10 @@ Client client = new Client(context)
Account account = new Account(client);
account.updatePrefs(
mapOf(
"language" to "en",
"timezone" to "UTC",
"darkTheme" to true
Map.of(
"language", "en",
"timezone", "UTC",
"darkTheme", true
), // prefs
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -13,7 +13,7 @@ Avatars avatars = new Avatars(client);
avatars.getScreenshot(
"https://example.com", // url
mapOf( "a" to "b" ), // headers (optional)
Map.of("a", "b"), // headers (optional)
1, // viewportWidth (optional)
1, // viewportHeight (optional)
0.1, // scale (optional)
@@ -26,7 +26,7 @@ avatars.getScreenshot(
-180, // longitude (optional)
0, // accuracy (optional)
false, // touch (optional)
listOf(), // permissions (optional)
List.of(), // permissions (optional)
0, // sleep (optional)
0, // width (optional)
0, // height (optional)
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,14 +14,14 @@ databases.createDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
mapOf(
"username" to "walter.obrien",
"email" to "walter.obrien@example.com",
"fullName" to "Walter O'Brien",
"age" to 30,
"isAdmin" to false
Map.of(
"username", "walter.obrien",
"email", "walter.obrien@example.com",
"fullName", "Walter O'Brien",
"age", 30,
"isAdmin", false
), // data
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -10,17 +10,15 @@ Databases databases = new Databases(client);
databases.createOperations(
"<TRANSACTION_ID>", // transactionId
listOf(
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": {
"name": "Walter O'Brien"
}
}
), // operations (optional)
List.of(Map.of(
"action", "create",
"databaseId", "<DATABASE_ID>",
"collectionId", "<COLLECTION_ID>",
"documentId", "<DOCUMENT_ID>",
"data", Map.of(
"name", "Walter O'Brien"
)
)), // operations (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,7 +12,7 @@ databases.getDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -11,7 +11,7 @@ Databases databases = new Databases(client);
databases.listDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -9,7 +9,7 @@ Client client = new Client(context)
Databases databases = new Databases(client);
databases.listTransactions(
listOf(), // queries (optional)
List.of(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,8 +14,8 @@ databases.updateDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
mapOf( "a" to "b" ), // data (optional)
listOf(Permission.read(Role.any())), // permissions (optional)
Map.of("a", "b"), // data (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,8 +14,8 @@ databases.upsertDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
mapOf( "a" to "b" ), // data
listOf(Permission.read(Role.any())), // permissions (optional)
Map.of("a", "b"), // data
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -15,7 +15,7 @@ functions.createExecution(
false, // async (optional)
"<PATH>", // path (optional)
ExecutionMethod.GET, // method (optional)
mapOf( "a" to "b" ), // headers (optional)
Map.of("a", "b"), // headers (optional)
"<SCHEDULED_AT>", // scheduledAt (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -10,7 +10,7 @@ Functions functions = new Functions(client);
functions.listExecutions(
"<FUNCTION_ID>", // functionId
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -9,7 +9,7 @@ Client client = new Client(context)
Graphql graphql = new Graphql(client);
graphql.mutation(
mapOf( "a" to "b" ), // query
Map.of("a", "b"), // query
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -9,7 +9,7 @@ Client client = new Client(context)
Graphql graphql = new Graphql(client);
graphql.query(
mapOf( "a" to "b" ), // query
Map.of("a", "b"), // query
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -1,9 +1,9 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.models.InputFile;
import io.appwrite.services.Storage;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Storage;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -15,7 +15,7 @@ storage.createFile(
"<BUCKET_ID>", // bucketId
"<FILE_ID>", // fileId
InputFile.fromPath("file.png"), // file
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,7 +10,7 @@ Storage storage = new Storage(client);
storage.listFiles(
"<BUCKET_ID>", // bucketId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Storage;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Storage;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,7 +14,7 @@ storage.updateFile(
"<BUCKET_ID>", // bucketId
"<FILE_ID>", // fileId
"<NAME>", // name (optional)
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,17 +10,15 @@ TablesDB tablesDB = new TablesDB(client);
tablesDB.createOperations(
"<TRANSACTION_ID>", // transactionId
listOf(
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"tableId": "<TABLE_ID>",
"rowId": "<ROW_ID>",
"data": {
"name": "Walter O'Brien"
}
}
), // operations (optional)
List.of(Map.of(
"action", "create",
"databaseId", "<DATABASE_ID>",
"tableId", "<TABLE_ID>",
"rowId", "<ROW_ID>",
"data", Map.of(
"name", "Walter O'Brien"
)
)), // operations (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,14 +14,14 @@ tablesDB.createRow(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
"<ROW_ID>", // rowId
mapOf(
"username" to "walter.obrien",
"email" to "walter.obrien@example.com",
"fullName" to "Walter O'Brien",
"age" to 30,
"isAdmin" to false
Map.of(
"username", "walter.obrien",
"email", "walter.obrien@example.com",
"fullName", "Walter O'Brien",
"age", 30,
"isAdmin", false
), // data
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -12,7 +12,7 @@ tablesDB.getRow(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
"<ROW_ID>", // rowId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -11,7 +11,7 @@ TablesDB tablesDB = new TablesDB(client);
tablesDB.listRows(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -9,7 +9,7 @@ Client client = new Client(context)
TablesDB tablesDB = new TablesDB(client);
tablesDB.listTransactions(
listOf(), // queries (optional)
List.of(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,8 +14,8 @@ tablesDB.updateRow(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
"<ROW_ID>", // rowId
mapOf( "a" to "b" ), // data (optional)
listOf(Permission.read(Role.any())), // permissions (optional)
Map.of("a", "b"), // data (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -14,8 +14,8 @@ tablesDB.upsertRow(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
"<ROW_ID>", // rowId
mapOf( "a" to "b" ), // data (optional)
listOf(Permission.read(Role.any())), // permissions (optional)
Map.of("a", "b"), // data (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -10,7 +10,7 @@ Teams teams = new Teams(client);
teams.createMembership(
"<TEAM_ID>", // teamId
listOf(), // roles
List.of(), // roles
"email@example.com", // email (optional)
"<USER_ID>", // userId (optional)
"+12065550100", // phone (optional)
@@ -11,7 +11,7 @@ Teams teams = new Teams(client);
teams.create(
"<TEAM_ID>", // teamId
"<NAME>", // name
listOf(), // roles (optional)
List.of(), // roles (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,7 +10,7 @@ Teams teams = new Teams(client);
teams.listMemberships(
"<TEAM_ID>", // teamId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -9,7 +9,7 @@ Client client = new Client(context)
Teams teams = new Teams(client);
teams.list(
listOf(), // queries (optional)
List.of(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -11,7 +11,7 @@ Teams teams = new Teams(client);
teams.updateMembership(
"<TEAM_ID>", // teamId
"<MEMBERSHIP_ID>", // membershipId
listOf(), // roles
List.of(), // roles
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,7 +10,7 @@ Teams teams = new Teams(client);
teams.updatePrefs(
"<TEAM_ID>", // teamId
mapOf( "a" to "b" ), // prefs
Map.of("a", "b"), // prefs
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,15 +10,13 @@ val databases = Databases(client)
val result = databases.createOperations(
transactionId = "<TRANSACTION_ID>",
operations = listOf(
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": {
"name": "Walter O'Brien"
}
}
), // (optional)
operations = listOf(mapOf(
"action" to "create",
"databaseId" to "<DATABASE_ID>",
"collectionId" to "<COLLECTION_ID>",
"documentId" to "<DOCUMENT_ID>",
"data" to mapOf(
"name" to "Walter O'Brien"
)
)), // (optional)
)
@@ -10,15 +10,13 @@ val tablesDB = TablesDB(client)
val result = tablesDB.createOperations(
transactionId = "<TRANSACTION_ID>",
operations = listOf(
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"tableId": "<TABLE_ID>",
"rowId": "<ROW_ID>",
"data": {
"name": "Walter O'Brien"
}
}
), // (optional)
operations = listOf(mapOf(
"action" to "create",
"databaseId" to "<DATABASE_ID>",
"tableId" to "<TABLE_ID>",
"rowId" to "<ROW_ID>",
"data" to mapOf(
"name" to "Walter O'Brien"
)
)), // (optional)
)
@@ -1,4 +1,4 @@
POST /v1/account/mfa/challenge HTTP/1.1
POST /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.8.0
@@ -1,4 +1,4 @@
PUT /v1/account/mfa/challenge HTTP/1.1
PUT /v1/account/mfa/challenges HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.8.0
@@ -1,4 +1,4 @@
import { Client, Functions, } from "@appwrite.io/console";
import { Client, Functions, TemplateReferenceType } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
@@ -11,7 +11,7 @@ const result = await functions.createTemplateDeployment({
repository: '<REPOSITORY>',
owner: '<OWNER>',
rootDirectory: '<ROOT_DIRECTORY>',
type: .Commit,
type: TemplateReferenceType.Commit,
reference: '<REFERENCE>',
activate: false // optional
});
@@ -1,4 +1,4 @@
import { Client, Functions, VCSDeploymentType } from "@appwrite.io/console";
import { Client, Functions, VCSReferenceType } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
@@ -8,7 +8,7 @@ const functions = new Functions(client);
const result = await functions.createVcsDeployment({
functionId: '<FUNCTION_ID>',
type: VCSDeploymentType.Branch,
type: VCSReferenceType.Branch,
reference: '<REFERENCE>',
activate: false // optional
});
@@ -1,4 +1,4 @@
import { Client, Sites, } from "@appwrite.io/console";
import { Client, Sites, TemplateReferenceType } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
@@ -11,7 +11,7 @@ const result = await sites.createTemplateDeployment({
repository: '<REPOSITORY>',
owner: '<OWNER>',
rootDirectory: '<ROOT_DIRECTORY>',
type: .Branch,
type: TemplateReferenceType.Branch,
reference: '<REFERENCE>',
activate: false // optional
});
@@ -1,4 +1,4 @@
import { Client, Sites, VCSDeploymentType } from "@appwrite.io/console";
import { Client, Sites, VCSReferenceType } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
@@ -8,7 +8,7 @@ const sites = new Sites(client);
const result = await sites.createVcsDeployment({
siteId: '<SITE_ID>',
type: VCSDeploymentType.Branch,
type: VCSReferenceType.Branch,
reference: '<REFERENCE>',
activate: false // optional
});
@@ -12,7 +12,7 @@ Deployment result = await functions.createTemplateDeployment(
repository: '<REPOSITORY>',
owner: '<OWNER>',
rootDirectory: '<ROOT_DIRECTORY>',
type: .commit,
type: TemplateReferenceType.commit,
reference: '<REFERENCE>',
activate: false, // (optional)
);
@@ -9,7 +9,7 @@ Functions functions = Functions(client);
Deployment result = await functions.createVcsDeployment(
functionId: '<FUNCTION_ID>',
type: VCSDeploymentType.branch,
type: VCSReferenceType.branch,
reference: '<REFERENCE>',
activate: false, // (optional)
);
@@ -12,7 +12,7 @@ Deployment result = await sites.createTemplateDeployment(
repository: '<REPOSITORY>',
owner: '<OWNER>',
rootDirectory: '<ROOT_DIRECTORY>',
type: .branch,
type: TemplateReferenceType.branch,
reference: '<REFERENCE>',
activate: false, // (optional)
);
@@ -9,7 +9,7 @@ Sites sites = Sites(client);
Deployment result = await sites.createVcsDeployment(
siteId: '<SITE_ID>',
type: VCSDeploymentType.branch,
type: VCSReferenceType.branch,
reference: '<REFERENCE>',
activate: false, // (optional)
);
@@ -15,7 +15,7 @@ Deployment result = await functions.CreateTemplateDeployment(
repository: "<REPOSITORY>",
owner: "<OWNER>",
rootDirectory: "<ROOT_DIRECTORY>",
type: .Commit,
type: TemplateReferenceType.Commit,
reference: "<REFERENCE>",
activate: false // optional
);
@@ -12,7 +12,7 @@ Functions functions = new Functions(client);
Deployment result = await functions.CreateVcsDeployment(
functionId: "<FUNCTION_ID>",
type: VCSDeploymentType.Branch,
type: VCSReferenceType.Branch,
reference: "<REFERENCE>",
activate: false // optional
);
@@ -15,7 +15,7 @@ Deployment result = await sites.CreateTemplateDeployment(
repository: "<REPOSITORY>",
owner: "<OWNER>",
rootDirectory: "<ROOT_DIRECTORY>",
type: .Branch,
type: TemplateReferenceType.Branch,
reference: "<REFERENCE>",
activate: false // optional
);
@@ -12,7 +12,7 @@ Sites sites = new Sites(client);
Deployment result = await sites.CreateVcsDeployment(
siteId: "<SITE_ID>",
type: VCSDeploymentType.Branch,
type: VCSReferenceType.Branch,
reference: "<REFERENCE>",
activate: false // optional
);
@@ -13,7 +13,7 @@ account.createOAuth2Token(
OAuthProvider.AMAZON, // provider
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
listOf(), // scopes (optional)
List.of(), // scopes (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,7 +10,7 @@ Client client = new Client()
Account account = new Account(client);
account.listIdentities(
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -10,7 +10,7 @@ Client client = new Client()
Account account = new Account(client);
account.listLogs(
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -10,10 +10,10 @@ Client client = new Client()
Account account = new Account(client);
account.updatePrefs(
mapOf(
"language" to "en",
"timezone" to "UTC",
"darkTheme" to true
Map.of(
"language", "en",
"timezone", "UTC",
"darkTheme", true
), // prefs
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -14,7 +14,7 @@ Avatars avatars = new Avatars(client);
avatars.getScreenshot(
"https://example.com", // url
mapOf( "a" to "b" ), // headers (optional)
Map.of("a", "b"), // headers (optional)
1, // viewportWidth (optional)
1, // viewportHeight (optional)
0.1, // scale (optional)
@@ -27,7 +27,7 @@ avatars.getScreenshot(
-180, // longitude (optional)
0, // accuracy (optional)
false, // touch (optional)
listOf(), // permissions (optional)
List.of(), // permissions (optional)
0, // sleep (optional)
0, // width (optional)
0, // height (optional)
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -15,7 +15,7 @@ databases.createCollection(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<NAME>", // name
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
false, // documentSecurity (optional)
false, // enabled (optional)
new CoroutineCallback<>((result, error) -> {
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -15,14 +15,14 @@ databases.createDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
mapOf(
"username" to "walter.obrien",
"email" to "walter.obrien@example.com",
"fullName" to "Walter O'Brien",
"age" to 30,
"isAdmin" to false
Map.of(
"username", "walter.obrien",
"email", "walter.obrien@example.com",
"fullName", "Walter O'Brien",
"age", 30,
"isAdmin", false
), // data
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -12,7 +12,7 @@ Databases databases = new Databases(client);
databases.createDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // documents
List.of(), // documents
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -13,7 +13,7 @@ databases.createEnumAttribute(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"", // key
listOf(), // elements
List.of(), // elements
false, // required
"<DEFAULT>", // default (optional)
false, // array (optional)
@@ -15,9 +15,9 @@ databases.createIndex(
"<COLLECTION_ID>", // collectionId
"", // key
IndexType.KEY, // type
listOf(), // attributes
listOf(), // orders (optional)
listOf(), // lengths (optional)
List.of(), // attributes
List.of(), // orders (optional)
List.of(), // lengths (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -14,7 +14,7 @@ databases.createLineAttribute(
"<COLLECTION_ID>", // collectionId
"", // key
false, // required
listOf([1, 2], [3, 4], [5, 6]), // default (optional)
List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6)), // default (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -11,17 +11,15 @@ Databases databases = new Databases(client);
databases.createOperations(
"<TRANSACTION_ID>", // transactionId
listOf(
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": {
"name": "Walter O'Brien"
}
}
), // operations (optional)
List.of(Map.of(
"action", "create",
"databaseId", "<DATABASE_ID>",
"collectionId", "<COLLECTION_ID>",
"documentId", "<DOCUMENT_ID>",
"data", Map.of(
"name", "Walter O'Brien"
)
)), // operations (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -14,7 +14,7 @@ databases.createPointAttribute(
"<COLLECTION_ID>", // collectionId
"", // key
false, // required
listOf(1, 2), // default (optional)
List.of(1, 2), // default (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -14,7 +14,7 @@ databases.createPolygonAttribute(
"<COLLECTION_ID>", // collectionId
"", // key
false, // required
listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // default (optional)
List.of(List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6), List.of(1, 2))), // default (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,7 +12,7 @@ Databases databases = new Databases(client);
databases.deleteDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -13,7 +13,7 @@ databases.getDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -12,7 +12,7 @@ Databases databases = new Databases(client);
databases.listAttributes(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -11,7 +11,7 @@ Databases databases = new Databases(client);
databases.listCollections(
"<DATABASE_ID>", // databaseId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -12,7 +12,7 @@ Databases databases = new Databases(client);
databases.listDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
List.of(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -12,7 +12,7 @@ Databases databases = new Databases(client);
databases.listIndexes(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
List.of(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
@@ -10,7 +10,7 @@ Client client = new Client()
Databases databases = new Databases(client);
databases.listTransactions(
listOf(), // queries (optional)
List.of(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,7 +10,7 @@ Client client = new Client()
Databases databases = new Databases(client);
databases.list(
listOf(), // queries (optional)
List.of(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -15,7 +15,7 @@ databases.updateCollection(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<NAME>", // name
listOf(Permission.read(Role.any())), // permissions (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
false, // documentSecurity (optional)
false, // enabled (optional)
new CoroutineCallback<>((result, error) -> {
@@ -1,8 +1,8 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
import io.appwrite.Permission;
import io.appwrite.Role;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
@@ -15,8 +15,8 @@ databases.updateDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
mapOf( "a" to "b" ), // data (optional)
listOf(Permission.read(Role.any())), // permissions (optional)
Map.of("a", "b"), // data (optional)
List.of(Permission.read(Role.any())), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {

Some files were not shown because too many files have changed in this diff Show More