Merge remote-tracking branch 'origin/1.8.x' into feat-apps-module-dl

This commit is contained in:
Damodar Lohani
2025-10-12 00:34:07 +00:00
1036 changed files with 43539 additions and 3348 deletions
+124
View File
@@ -2521,4 +2521,128 @@ return [
],
],
],
'transactions' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('transactions'),
'name' => 'Transactions',
'attributes' => [
[
'$id' => ID::custom('status'),
'type' => Database::VAR_STRING,
'size' => 16, // pending | committing | committed | failed
'signed' => true,
'required' => false,
'default' => 'pending',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('operations'),
'type' => Database::VAR_INTEGER,
'size' => 0,
'signed' => false,
'required' => true,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('expiresAt'),
'type' => Database::VAR_DATETIME,
'size' => 0,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_expiresAt'),
'type' => Database::INDEX_KEY,
'attributes' => ['expiresAt'],
'lengths' => [],
'orders' => [Database::ORDER_DESC],
],
],
],
'transactionLogs' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('transactionLogs'),
'name' => 'Transaction Logs',
'attributes' => [
[
'$id' => ID::custom('transactionInternalId'),
'type' => Database::VAR_STRING,
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('databaseInternalId'),
'type' => Database::VAR_STRING,
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('collectionInternalId'),
'type' => Database::VAR_STRING,
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('documentId'),
'type' => Database::VAR_STRING,
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('action'),
'type' => Database::VAR_STRING,
'size' => 32, // create | update | upsert | increment | decrement | delete | bulkCreate | bulkUpdate | bulkUpsert | bulkDelete
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('data'),
'type' => Database::VAR_STRING,
'size' => 5_000_000, // Allow large payloads for bulk operations
'signed' => false,
'required' => true,
'default' => null,
'array' => false,
'filters' => ['json'],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_transaction'),
'type' => Database::INDEX_KEY,
'attributes' => ['transactionInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
];
+1
View File
@@ -48,6 +48,7 @@ $console = [
'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
],
'smtpBaseTemplate' => APP_BRANDED_EMAIL_BASE_TEMPLATE,
];
return $console;
+57
View File
@@ -211,6 +211,11 @@ return [
'description' => 'User with the requested ID could not be found.',
'code' => 404,
],
Exception::USER_EMAIL_NOT_FOUND => [
'name' => Exception::USER_EMAIL_NOT_FOUND,
'description' => 'User email could not be found.',
'code' => 400,
],
Exception::USER_EMAIL_ALREADY_EXISTS => [
'name' => Exception::USER_EMAIL_ALREADY_EXISTS,
'description' => 'A user with the same email already exists in the current project.',
@@ -312,11 +317,21 @@ return [
'description' => 'OAuth2 provider returned some error.',
'code' => 424,
],
Exception::USER_EMAIL_NOT_VERIFIED => [
'name' => Exception::USER_EMAIL_NOT_VERIFIED,
'description' => 'User email is not verified',
'code' => 400,
],
Exception::USER_EMAIL_ALREADY_VERIFIED => [
'name' => Exception::USER_EMAIL_ALREADY_VERIFIED,
'description' => 'User email is already verified',
'code' => 409,
],
Exception::USER_PHONE_NOT_VERIFIED => [
'name' => Exception::USER_PHONE_NOT_VERIFIED,
'description' => 'User phone is not verified',
'code' => 400,
],
Exception::USER_PHONE_ALREADY_VERIFIED => [
'name' => Exception::USER_PHONE_ALREADY_VERIFIED,
'description' => 'User phone is already verified',
@@ -966,6 +981,48 @@ return [
'code' => 409,
],
/** Transactions */
Exception::TRANSACTION_NOT_FOUND => [
'name' => Exception::TRANSACTION_NOT_FOUND,
'description' => 'Transaction with the requested ID could not be found.',
'code' => 404,
],
Exception::TRANSACTION_ALREADY_EXISTS => [
'name' => Exception::TRANSACTION_ALREADY_EXISTS,
'description' => 'Transaction with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.',
'code' => 409,
],
Exception::TRANSACTION_INVALID => [
'name' => Exception::TRANSACTION_INVALID,
'description' => 'The transaction is invalid. Please check the transaction state and try again.',
'code' => 400,
],
Exception::TRANSACTION_FAILED => [
'name' => Exception::TRANSACTION_FAILED,
'description' => 'The transaction has errored. Please check the transaction data and try again.',
'code' => 400,
],
Exception::TRANSACTION_EXPIRED => [
'name' => Exception::TRANSACTION_EXPIRED,
'description' => 'The transaction has expired. Please create a new transaction and try again.',
'code' => 410,
],
Exception::TRANSACTION_CONFLICT => [
'name' => Exception::TRANSACTION_CONFLICT,
'description' => 'The transaction has a conflict. Please resolve the conflict and try again.',
'code' => 409,
],
Exception::TRANSACTION_LIMIT_EXCEEDED => [
'name' => Exception::TRANSACTION_LIMIT_EXCEEDED,
'description' => 'The maximum number of operations per transaction has been exceeded.',
'code' => 400,
],
Exception::TRANSACTION_NOT_READY => [
'name' => Exception::TRANSACTION_NOT_READY,
'description' => 'The transaction is not ready yet. Please try again later.',
'code' => 400,
],
/** Project Errors */
Exception::PROJECT_NOT_FOUND => [
'name' => Exception::PROJECT_NOT_FOUND,
@@ -131,6 +131,14 @@
.social-icon > img {
margin: auto;
}
p.security-phrase:not(:empty) {
opacity: 0.7;
margin: 0;
padding: 0;
margin-top: 32px;
padding-top: 32px;
border-top: 1px solid #e8e9f0;
}
</style>
</head>
@@ -147,6 +155,7 @@
<img
height="32px"
src="{{logoUrl}}"
alt="Appwrite logo"
/>
</td>
</tr>
@@ -155,12 +164,12 @@
<table style="margin-top: 32px">
<tr>
<td>
<h1>{{subject}}</h1>
<h1>{{heading}}</h1>
</td>
</tr>
</table>
<table style="margin-top: 32px">
<table style="margin-top: 16px">
<tr>
<td>
{{body}}
+21 -1
View File
@@ -44,6 +44,21 @@
color: currentColor;
word-break: break-all;
}
a.button {
box-sizing: border-box;
display: inline-block;
text-align: center;
text-decoration: none;
padding: 9px 14px;
color: #ffffff;
background-color: #2D2D31;
border: 1px solid #414146;
border-radius: 8px;
}
a.button:hover,
a.button:focus {
opacity: 0.8;
}
table {
width: 100%;
border-spacing: 0 !important;
@@ -94,10 +109,15 @@
h* {
font-family: 'Poppins', sans-serif;
}
p {
margin-bottom: 10px;
}
p.security-phrase:not(:empty) {
opacity: 0.7;
margin-top: 32px;
padding-top: 32px;
border-top: 1px solid #e8e9f0;
}
</style>
</head>
@@ -1,6 +1,6 @@
<p>{{hello}}</p>
<p>{{body}}</p>
<p><a href="{{redirect}}" target="_blank" style="font-size: 14px; font-family: Inter, sans-serif; color: #ffffff; text-decoration: none; background-color: #2D2D31; border-radius: 8px; padding: 9px 14px; border: 1px solid #414146; display: inline-block; text-align:center; box-sizing: border-box;">{{buttonText}}</a></p>
<p><a href="{{redirect}}" target="_blank" class="button">{{buttonText}}</a></p>
<p>{{footer}}</p>
<p style="margin-bottom: 32px">
{{thanks}}
@@ -5,7 +5,7 @@
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; display: inline-block;">
<tr>
<td align="center" style="border-radius: 8px; background-color: #19191D;">
<a rel="noopener" target="_blank" href="{{redirect}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; border-radius: 8px; padding: 9px 14px; border: 1px solid #19191D; display: inline-block;">{{buttonText}}</a>
<a rel="noopener" target="_blank" href="{{redirect}}" class="button">{{buttonText}}</a>
</td>
</tr>
</table>
@@ -5,7 +5,7 @@
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; display: inline-block;">
<tr>
<td align="center" style="border-radius: 8px; background-color: #ffffff;">
<p style="font-size: 24px; text-indent: 18px; letter-spacing: 18px; font-family: 'Inter', sans-serif; color: #414146; text-decoration: none; border-radius: 8px; padding: 24px 12px; border: 1px solid #EDEDF0; display: inline-block; font-weight: bold; ">{{otp}}</p>
<p style="font-size: 24px; text-indent: 18px; letter-spacing: 18px; font-family: 'Inter', sans-serif; color: #414146; text-decoration: none; border-radius: 8px; margin-top: 0px; margin-bottom: 0px; padding: 24px 12px; border: 1px solid #EDEDF0; display: inline-block; font-weight: bold;">{{otp}}</p>
</td>
</tr>
</table>
+2 -4
View File
@@ -5,7 +5,7 @@
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; display: inline-block;">
<tr>
<td align="center" style="border-radius: 8px; background-color: #ffffff;">
<p style="font-size: 24px; text-indent: 18px; letter-spacing: 18px; font-family: Inter; color: #414146; text-decoration: none; border-radius: 8px; padding: 24px 12px; border: 1px solid #EDEDF0; display: inline-block; font-weight: bold; ">{{otp}}</p>
<p style="font-size: 24px; text-indent: 18px; letter-spacing: 18px; font-family: 'Inter', sans-serif; color: #414146; text-decoration: none; border-radius: 8px; margin-top: 0px; margin-bottom: 0px; padding: 24px 12px; border: 1px solid #EDEDF0; display: inline-block; font-weight: bold; ">{{otp}}</p>
</td>
</tr>
</table>
@@ -15,6 +15,4 @@
<p style="margin-bottom: 0px;">{{thanks}}</p>
<p style="margin-top: 0px;">{{signature}}</p>
<hr style="margin-block-start: 1rem; margin-block-end: 1rem;">
<p style="opacity: 0.7;">{{securityPhrase}}</p>
<p class="security-phrase">{{securityPhrase}}</p>
+5 -2
View File
@@ -3,8 +3,9 @@
"settings.locale": "en",
"settings.direction": "ltr",
"emails.sender": "{{project}} Team",
"emails.verification.subject": "Account Verification",
"emails.verification.subject": "Account Verification for {{project}}",
"emails.verification.preview": "Verify your email to activate your {{project}} account.",
"emails.verification.heading": "Verify your email to start using {{project}}",
"emails.verification.hello": "Hello {{user}},",
"emails.verification.body": "Follow this link to verify your email address to your {{b}}{{project}}{{/b}} account.",
"emails.verification.footer": "If you didnt ask to verify this address, you can ignore this message.",
@@ -33,6 +34,7 @@
"emails.sessionAlert.signature": "{{project}} team",
"emails.otpSession.subject": "OTP for {{project}} Login",
"emails.otpSession.preview": "Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.",
"emails.otpSession.heading": "Login with OTP to use {{project}}",
"emails.otpSession.hello": "Hello {{user}},",
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.",
"emails.otpSession.clientInfo": "This sign in was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the sign in, you can safely ignore this email.",
@@ -41,12 +43,13 @@
"emails.otpSession.signature": "{{project}} team",
"emails.mfaChallenge.subject": "Verification Code for {{project}}",
"emails.mfaChallenge.preview": "Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.",
"emails.mfaChallenge.heading": "Complete two-step verification to use {{project}}",
"emails.mfaChallenge.hello": "Hello {{user}},",
"emails.mfaChallenge.description": "Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.",
"emails.mfaChallenge.clientInfo": "This verification code was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the verification code, you can safely ignore this email.",
"emails.mfaChallenge.thanks": "Thanks,",
"emails.mfaChallenge.signature": "{{project}} team",
"emails.recovery.subject": "Password Reset",
"emails.recovery.subject": "Password Reset for {{project}}",
"emails.recovery.preview": "Reset your {{project}} password using the link.",
"emails.recovery.hello": "Hello {{user}},",
"emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.",
+16 -16
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '20.0.0',
'version' => '21.2.1',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -60,7 +60,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '19.0.0',
'version' => '20.2.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -79,7 +79,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '12.0.0',
'version' => '13.2.1',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -116,7 +116,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '10.0.0',
'version' => '11.2.1',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -139,7 +139,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.13.0',
'version' => '0.17.1',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -207,7 +207,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.1.0',
'version' => '0.1.1',
'url' => '',
'package' => '',
'enabled' => true,
@@ -226,7 +226,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '9.1.0',
'version' => '10.2.1',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -262,7 +262,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '19.0.0',
'version' => '20.2.1',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -281,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '17.0.0',
'version' => '17.4.1',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -300,7 +300,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '13.0.0',
'version' => '13.4.1',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -319,7 +319,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '18.0.0',
'version' => '19.2.1',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -338,7 +338,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '0.11.0',
'version' => 'v0.13.1',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -357,7 +357,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.17.0',
'version' => '0.21.1',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -376,7 +376,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '18.0.0',
'version' => '19.2.1',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -395,7 +395,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '11.0.0',
'version' => '12.2.1',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -418,7 +418,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '12.0.0',
'version' => '13.2.1',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
+1 -1
View File
@@ -27,7 +27,7 @@ $member = [
'subscribers.write',
'subscribers.read',
'assistant.read',
'rules.read'
'rules.read',
];
$admins = [
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+176 -50
View File
@@ -63,6 +63,7 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Utopia\Storage\Validator\FileName;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
@@ -170,7 +171,8 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
->setVariables($emailVariables)
->setRecipient($email)
->trigger();
};
}
;
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken) {
@@ -856,7 +858,7 @@ App::patch('/v1/account/sessions/:sessionId')
$session
->setAttribute('providerAccessToken', $oauth2->getAccessToken(''))
->setAttribute('providerRefreshToken', $oauth2->getRefreshToken(''))
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $oauth2->getAccessTokenExpiry('')));
}
// Save changes
@@ -1009,9 +1011,11 @@ App::post('/v1/account/sessions/email')
;
if ($project->getAttribute('auths', [])['sessionAlerts'] ?? false) {
if ($dbForProject->count('sessions', [
Query::equal('userId', [$user->getId()]),
]) !== 1) {
if (
$dbForProject->count('sessions', [
Query::equal('userId', [$user->getId()]),
]) !== 1
) {
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
}
}
@@ -1128,7 +1132,7 @@ App::post('/v1/account/sessions/anonymous')
Authorization::setRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
@@ -1699,13 +1703,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'providerEmail' => $email,
'providerAccessToken' => $accessToken,
'providerRefreshToken' => $refreshToken,
'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry),
'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry),
]));
} else {
$identity
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry));
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry));
$dbForProject->updateDocument('identities', $identity->getId(), $identity);
}
@@ -2345,7 +2349,17 @@ App::post('/v1/account/tokens/email')
$subject = $locale->getText("emails.otpSession.subject");
$preview = $locale->getText("emails.otpSession.preview");
$heading = $locale->getText("emails.otpSession.heading");
$customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $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();
@@ -2415,6 +2429,7 @@ App::post('/v1/account/tokens/email')
}
$emailVariables = [
'heading' => $heading,
'direction' => $locale->getText('settings.direction'),
// {{user}}, {{project}} and {{otp}} are required in the templates
'user' => $user->getAttribute('name'),
@@ -2428,10 +2443,23 @@ App::post('/v1/account/tokens/email')
'team' => '',
];
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($email)
->trigger();
@@ -2792,10 +2820,12 @@ App::post('/v1/account/jwts')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
'userId' => $user->getId(),
'sessionId' => $sessionId,
])]), Response::MODEL_JWT);
->dynamic(new Document([
'jwt' => $jwt->encode([
'userId' => $user->getId(),
'sessionId' => $current->getId(),
])
]), Response::MODEL_JWT);
});
App::get('/v1/account/prefs')
@@ -3585,27 +3615,48 @@ App::put('/v1/account/recovery')
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
});
App::post('/v1/account/verification')
App::post('/v1/account/verifications/email')
->alias('/v1/account/verification')
->desc('Create email verification')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'users.[userId].verification.[tokenId].create')
->label('audits.event', 'verification.create')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createVerification',
description: '/docs/references/account/create-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON,
))
->label('sdk', [
new Method(
namespace: 'account',
group: 'verification',
name: 'createEmailVerification',
description: '/docs/references/account/create-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON,
),
new Method(
namespace: 'account',
group: 'verification',
name: 'createVerification',
description: '/docs/references/account/create-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.createEmailVerification'
),
)
])
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{userId}')
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey']) // TODO add built-in confirm page
@@ -3624,6 +3675,10 @@ App::post('/v1/account/verification')
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
if (empty($user->getAttribute('email'))) {
throw new Exception(Exception::USER_EMAIL_NOT_FOUND);
}
$url = htmlentities($url);
if ($user->getAttribute('emailVerification')) {
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
@@ -3662,7 +3717,17 @@ App::post('/v1/account/verification')
$body = $locale->getText("emails.verification.body");
$preview = $locale->getText("emails.verification.preview");
$subject = $locale->getText("emails.verification.subject");
$heading = $locale->getText("emails.verification.heading");
$customTemplate = $project->getAttribute('templates', [])['email.verification-' . $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';
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
$message
@@ -3722,6 +3787,7 @@ App::post('/v1/account/verification')
}
$emailVariables = [
'heading' => $heading,
'direction' => $locale->getText('settings.direction'),
// {{user}}, {{redirect}} and {{project}} are required in default and custom templates
'user' => $user->getAttribute('name'),
@@ -3731,10 +3797,23 @@ App::post('/v1/account/verification')
'team' => '',
];
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'))
->setName($user->getAttribute('name') ?? '')
@@ -3752,27 +3831,48 @@ App::post('/v1/account/verification')
->dynamic($verification, Response::MODEL_TOKEN);
});
App::put('/v1/account/verification')
App::put('/v1/account/verifications/email')
->alias('/v1/account/verification')
->desc('Update email verification (confirmation)')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].verification.[tokenId].update')
->label('audits.event', 'verification.update')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updateVerification',
description: '/docs/references/account/update-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON
))
->label('sdk', [
new Method(
namespace: 'account',
group: 'verification',
name: 'updateEmailVerification',
description: '/docs/references/account/update-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON
),
new Method(
namespace: 'account',
group: 'verification',
name: 'updateVerification',
description: '/docs/references/account/update-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_TOKEN,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'account.updateEmailVerification'
),
)
])
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
@@ -3820,7 +3920,8 @@ App::put('/v1/account/verification')
$response->dynamic($verification, Response::MODEL_TOKEN);
});
App::post('/v1/account/verification/phone')
App::post('/v1/account/verifications/phone')
->alias('/v1/account/verification/phone')
->desc('Create phone verification')
->groups(['api', 'account', 'auth'])
->label('scope', 'account')
@@ -3970,7 +4071,8 @@ App::post('/v1/account/verification/phone')
->dynamic($verification, Response::MODEL_TOKEN);
});
App::put('/v1/account/verification/phone')
App::put('/v1/account/verifications/phone')
->alias('/v1/account/verification/phone')
->desc('Update phone verification (confirmation)')
->groups(['api', 'account'])
->label('scope', 'public')
@@ -4746,7 +4848,17 @@ App::post('/v1/account/mfa/challenge')
$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();
@@ -4810,6 +4922,7 @@ App::post('/v1/account/mfa/challenge')
}
$emailVariables = [
'heading' => $heading,
'direction' => $locale->getText('settings.direction'),
// {{user}}, {{project}} and {{otp}} are required in the templates
'user' => $user->getAttribute('name'),
@@ -4817,13 +4930,26 @@ App::post('/v1/account/mfa/challenge')
'otp' => $code,
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
'agentOs' => $agentOs['osName'] ?? '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();
@@ -4946,8 +5072,8 @@ App::put('/v1/account/mfa/challenge')
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId());
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId());
$response->dynamic($session, Response::MODEL_SESSION);
});
@@ -5012,7 +5138,7 @@ App::post('/v1/account/targets/push')
],
'providerId' => !empty($providerId) ? $providerId : null,
'providerInternalId' => !empty($providerId) ? $provider->getSequence() : null,
'providerType' => MESSAGE_TYPE_PUSH,
'providerType' => MESSAGE_TYPE_PUSH,
'userId' => $user->getId(),
'userInternalId' => $user->getSequence(),
'sessionId' => $session->getId(),
@@ -5187,8 +5313,8 @@ App::get('/v1/account/identities')
$queries[] = Query::equal('userInternalId', [$user->getSequence()]);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
*/
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
+6 -6
View File
@@ -3102,7 +3102,7 @@ App::post('/v1/messaging/messages/email')
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE,
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getSequence(),
'resourceUpdatedAt' => DateTime::now(),
@@ -3244,7 +3244,7 @@ App::post('/v1/messaging/messages/sms')
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE,
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getSequence(),
'resourceUpdatedAt' => DateTime::now(),
@@ -3462,7 +3462,7 @@ App::post('/v1/messaging/messages/push')
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE,
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getSequence(),
'resourceUpdatedAt' => DateTime::now(),
@@ -3863,7 +3863,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE,
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getSequence(),
'resourceUpdatedAt' => DateTime::now(),
@@ -4084,7 +4084,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE,
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getSequence(),
'resourceUpdatedAt' => DateTime::now(),
@@ -4258,7 +4258,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE,
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getSequence(),
'resourceUpdatedAt' => DateTime::now(),
+22 -1
View File
@@ -1755,7 +1755,28 @@ App::post('/v1/projects/:projectId/platforms')
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', null, new WhiteList([Platform::TYPE_WEB, Platform::TYPE_FLUTTER_WEB, Platform::TYPE_FLUTTER_IOS, Platform::TYPE_FLUTTER_ANDROID, Platform::TYPE_FLUTTER_LINUX, Platform::TYPE_FLUTTER_MACOS, Platform::TYPE_FLUTTER_WINDOWS, Platform::TYPE_APPLE_IOS, Platform::TYPE_APPLE_MACOS, Platform::TYPE_APPLE_WATCHOS, Platform::TYPE_APPLE_TVOS, Platform::TYPE_ANDROID, Platform::TYPE_UNITY, Platform::TYPE_REACT_NATIVE_IOS, Platform::TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
->param(
'type',
null,
new WhiteList([
Platform::TYPE_WEB,
Platform::TYPE_FLUTTER_WEB,
Platform::TYPE_FLUTTER_IOS,
Platform::TYPE_FLUTTER_ANDROID,
Platform::TYPE_FLUTTER_LINUX,
Platform::TYPE_FLUTTER_MACOS,
Platform::TYPE_FLUTTER_WINDOWS,
Platform::TYPE_APPLE_IOS,
Platform::TYPE_APPLE_MACOS,
Platform::TYPE_APPLE_WATCHOS,
Platform::TYPE_APPLE_TVOS,
Platform::TYPE_ANDROID,
Platform::TYPE_UNITY,
Platform::TYPE_REACT_NATIVE_IOS,
Platform::TYPE_REACT_NATIVE_ANDROID,
], true),
'Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.'
)
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
+2 -1
View File
@@ -1265,7 +1265,7 @@ App::error()
}
/**
* If its not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
* If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
*/
if (!$publish && $project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
@@ -1367,6 +1367,7 @@ App::error()
case 409: // Error allowed publicly
case 412: // Error allowed publicly
case 416: // Error allowed publicly
case 422: // Error allowed publicly
case 429: // Error allowed publicly
case 451: // Error allowed publicly
case 501: // Error allowed publicly
+15 -1
View File
@@ -31,6 +31,7 @@ const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
const APP_LIMIT_DATABASE_BATCH = 100; // Default maximum batch size for database operations
const APP_LIMIT_DATABASE_TRANSACTION = 100; // Default maximum operations per transaction
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
@@ -55,6 +56,10 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
const APP_DATABASE_QUERY_MAX_VALUES = 500;
const APP_DATABASE_ENCRYPT_SIZE_MIN = 150;
const APP_DATABASE_TXN_TTL_MIN = 60; // 1 minute
const APP_DATABASE_TXN_TTL_MAX = 3600; // 1 hour
const APP_DATABASE_TXN_TTL_DEFAULT = 300; // 5 minutes
const APP_DATABASE_TXN_MAX_OPERATIONS = 100; // Maximum operations per transaction
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_SITES = '/storage/sites';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
@@ -85,6 +90,7 @@ const APP_PLATFORM_CLIENT = 'client';
const APP_PLATFORM_CONSOLE = 'console';
const APP_VCS_GITHUB_USERNAME = 'Appwrite';
const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io';
const APP_BRANDED_EMAIL_BASE_TEMPLATE = 'email-base-styled';
// User Roles
const USER_ROLE_ANY = 'any';
@@ -170,8 +176,11 @@ const BUILD_TYPE_RETRY = 'retry';
// Deletion Types
const DELETE_TYPE_DATABASES = 'databases';
const DELETE_TYPE_DOCUMENT = 'document';
const DELETE_TYPE_COLLECTIONS = 'collections';
const DELETE_TYPE_TRANSACTION = 'transaction';
const DELETE_TYPE_EXPIRED_TRANSACTIONS = 'expired_transactions';
const DELETE_TYPE_PROJECTS = 'projects';
const DELETE_TYPE_SITES = 'sites';
const DELETE_TYPE_FUNCTIONS = 'functions';
@@ -337,10 +346,15 @@ const RESOURCE_TYPE_PROVIDERS = 'providers';
const RESOURCE_TYPE_TOPICS = 'topics';
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
const RESOURCE_TYPE_MESSAGES = 'messages';
const RESOURCE_TYPE_EXECUTIONS = 'executions';
// Resource types for Tokens
const TOKENS_RESOURCE_TYPE_FILES = 'files';
const TOKENS_RESOURCE_TYPE_SITES = 'sites';
const TOKENS_RESOURCE_TYPE_FUNCTIONS = 'functions';
const TOKENS_RESOURCE_TYPE_DATABASES = 'databases';
// Resource types for Schedules
const SCHEDULE_RESOURCE_TYPE_EXECUTION = 'execution';
const SCHEDULE_RESOURCE_TYPE_FUNCTION = 'function';
const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message';
+2
View File
@@ -255,6 +255,8 @@ Database::addFilter(
->find('variables', [
Query::equal('resourceInternalId', [$document->getSequence()]),
Query::equal('resourceType', $resourceType),
Query::orderAsc('resourceType'),
Query::orderAsc(),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
+43 -27
View File
@@ -4,6 +4,7 @@ use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Key;
use Appwrite\Databases\TransactionState;
use Appwrite\Event\Audit;
use Appwrite\Event\Build;
use Appwrite\Event\Certificate;
@@ -157,7 +158,7 @@ App::setResource('queueForMigrations', function (Publisher $publisher) {
App::setResource('queueForStatsResources', function (Publisher $publisher) {
return new StatsResources($publisher);
}, ['publisher']);
App::setResource('platforms', function (Request $request, Document $console, Document $project) {
App::setResource('platforms', function (Request $request, Document $console, Document $project, Database $dbForPlatform) {
$console->setAttribute('platforms', [ // Always allow current host
'$collection' => ID::custom('platforms'),
'name' => 'Current Host',
@@ -196,11 +197,40 @@ App::setResource('platforms', function (Request $request, Document $console, Doc
], Document::SET_TYPE_APPEND);
}
$origin = \parse_url($request->getOrigin(), PHP_URL_HOST);
if (empty($origin)) {
$origin = \parse_url($request->getReferer(), PHP_URL_HOST);
}
// Safe if rule with same project ID exists
if (!empty($origin)) {
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
])
)[0] ?? new Document();
}
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
$project->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Platform::TYPE_WEB,
'name' => $origin,
'hostname' => $origin,
], Document::SET_TYPE_APPEND);
}
}
return [
...$console->getAttribute('platforms', []),
...$project->getAttribute('platforms', []),
];
}, ['request', 'console', 'project']);
}, ['request', 'console', 'project', 'dbForPlatform']);
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform, Store $store, Token $proofForToken) {
/** @var Appwrite\Utopia\Request $request */
@@ -398,7 +428,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setTenant((int) $project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
@@ -451,7 +481,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setTenant((int) $project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
@@ -481,7 +511,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return function (?Document $project = null) use ($pools, $cache, &$database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
$database->setTenant((int) $project->getSequence());
return $database;
}
@@ -496,7 +526,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
// set tenant
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
$database->setTenant((int) $project->getSequence());
}
return $database;
@@ -524,7 +554,7 @@ App::setResource('redis', function () {
$pass = System::getEnv('_APP_REDIS_PASS', '');
$redis = new \Redis();
@$redis->pconnect($host, (int)$port);
@$redis->pconnect($host, (int) $port);
if ($pass) {
$redis->auth($pass);
}
@@ -737,7 +767,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
// NOTE: `params` and `urls` are not used internally in the `Schema::build` function below!
$params = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return [ 'queries' => $args['queries']];
return ['queries' => $args['queries']];
},
'create' => function (string $databaseId, string $collectionId, array $args) {
$id = $args['id'] ?? 'unique()';
@@ -1017,7 +1047,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
}
$accessedAt = $token->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), - APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
$token->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
}
@@ -1059,24 +1089,6 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
return $referrer;
}
// Safe if rule with same project ID exists
if (!empty($origin)) {
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
])
)[0] ?? new Document();
}
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
return $referrer;
}
}
// Unsafe; Localhost is always safe for ease of local development
$origin = 'localhost';
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
@@ -1084,3 +1096,7 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
return $referrer;
}, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']);
App::setResource('transactionState', function (Database $dbForProject) {
return new TransactionState($dbForProject);
}, ['dbForProject']);
+22 -3
View File
@@ -607,11 +607,18 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$code = 500;
}
$message = $th->getMessage();
// sanitize 0 && 5xx errors
if (($code === 0 || $code >= 500) && !App::isDevelopment()) {
$message = 'Error: Server Error';
}
$response = [
'type' => 'error',
'data' => [
'code' => $code,
'message' => $th->getMessage()
'message' => $message
]
];
@@ -717,11 +724,23 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Message type is not valid.');
}
} catch (Throwable $th) {
$code = $th->getCode();
if (!is_int($code)) {
$code = 500;
}
$message = $th->getMessage();
// sanitize 0 && 5xx errors
if (($code === 0 || $code >= 500) && !App::isDevelopment()) {
$message = 'Error: Server Error';
}
$response = [
'type' => 'error',
'data' => [
'code' => $th->getCode(),
'message' => $th->getMessage()
'code' => $code,
'message' => $message
]
];
+1 -1
View File
@@ -53,7 +53,7 @@
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "1.*",
"utopia-php/database": "2.*",
"utopia-php/detector": "0.1.*",
"utopia-php/domains": "0.8.*",
"utopia-php/dns": "0.3.*",
Generated
+146 -137
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": "883816d2ccfa5372c8c3bb0504dde205",
"content-hash": "a314e9f9a50c2564330d82ad10041f99",
"packages": [
{
"name": "adhocore/jwt",
@@ -1159,20 +1159,20 @@
},
{
"name": "open-telemetry/api",
"version": "1.5.0",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/api.git",
"reference": "7692075f486c14d8cfd37fba98a08a5667f089e5"
"reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/7692075f486c14d8cfd37fba98a08a5667f089e5",
"reference": "7692075f486c14d8cfd37fba98a08a5667f089e5",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522",
"reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522",
"shasum": ""
},
"require": {
"open-telemetry/context": "^1.0",
"open-telemetry/context": "^1.4",
"php": "^8.1",
"psr/log": "^1.1|^2.0|^3.0",
"symfony/polyfill-php82": "^1.26"
@@ -1188,7 +1188,7 @@
]
},
"branch-alias": {
"dev-main": "1.4.x-dev"
"dev-main": "1.7.x-dev"
}
},
"autoload": {
@@ -1225,20 +1225,20 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-08-07T23:07:38+00:00"
"time": "2025-10-02T23:44:28+00:00"
},
{
"name": "open-telemetry/context",
"version": "1.3.1",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/context.git",
"reference": "438f71812242db3f196fb4c717c6f92cbc819be6"
"reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6",
"reference": "438f71812242db3f196fb4c717c6f92cbc819be6",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/d4c4470b541ce72000d18c339cfee633e4c8e0cf",
"reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf",
"shasum": ""
},
"require": {
@@ -1284,7 +1284,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-08-13T01:12:00+00:00"
"time": "2025-09-19T00:05:49+00:00"
},
{
"name": "open-telemetry/exporter-otlp",
@@ -1352,16 +1352,16 @@
},
{
"name": "open-telemetry/gen-otlp-protobuf",
"version": "1.5.0",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git",
"reference": "585bafddd4ae6565de154610b10a787a455c9ba0"
"reference": "673af5b06545b513466081884b47ef15a536edde"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/585bafddd4ae6565de154610b10a787a455c9ba0",
"reference": "585bafddd4ae6565de154610b10a787a455c9ba0",
"url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/673af5b06545b513466081884b47ef15a536edde",
"reference": "673af5b06545b513466081884b47ef15a536edde",
"shasum": ""
},
"require": {
@@ -1411,27 +1411,27 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-01-15T23:07:07+00:00"
"time": "2025-09-17T23:10:12+00:00"
},
{
"name": "open-telemetry/sdk",
"version": "1.7.1",
"version": "1.9.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sdk.git",
"reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06"
"reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/52690d4b37ae4f091af773eef3c238ed2bc0aa06",
"reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e",
"reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e",
"shasum": ""
},
"require": {
"ext-json": "*",
"nyholm/psr7-server": "^1.1",
"open-telemetry/api": "^1.4",
"open-telemetry/context": "^1.0",
"open-telemetry/api": "^1.7",
"open-telemetry/context": "^1.4",
"open-telemetry/sem-conv": "^1.0",
"php": "^8.1",
"php-http/discovery": "^1.14",
@@ -1465,7 +1465,7 @@
]
},
"branch-alias": {
"dev-main": "1.0.x-dev"
"dev-main": "1.9.x-dev"
}
},
"autoload": {
@@ -1508,7 +1508,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-09-05T07:17:06+00:00"
"time": "2025-10-02T23:44:28+00:00"
},
{
"name": "open-telemetry/sem-conv",
@@ -1569,16 +1569,16 @@
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.7.0",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105"
"reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105",
"reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226",
"reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226",
"shasum": ""
},
"require": {
@@ -1632,7 +1632,7 @@
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"time": "2024-05-08T12:18:48+00:00"
"time": "2025-09-24T15:12:37+00:00"
},
{
"name": "paragonie/random_compat",
@@ -1927,16 +1927,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.46",
"version": "3.0.47",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
"reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d",
"reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d",
"shasum": ""
},
"require": {
@@ -2017,7 +2017,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.47"
},
"funding": [
{
@@ -2033,7 +2033,7 @@
"type": "tidelift"
}
],
"time": "2025-06-26T16:29:55+00:00"
"time": "2025-10-06T01:07:24+00:00"
},
{
"name": "psr/container",
@@ -2596,16 +2596,16 @@
},
{
"name": "symfony/http-client",
"version": "v7.3.3",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019"
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019",
"reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019",
"url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62",
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62",
"shasum": ""
},
"require": {
@@ -2672,7 +2672,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.3"
"source": "https://github.com/symfony/http-client/tree/v7.3.4"
},
"funding": [
{
@@ -2692,7 +2692,7 @@
"type": "tidelift"
}
],
"time": "2025-08-27T07:45:05+00:00"
"time": "2025-09-11T10:12:26+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -3293,16 +3293,16 @@
},
{
"name": "utopia-php/abuse",
"version": "1.0.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "c5e2232033b507a07f72180dc56d37e1872ee7be"
"reference": "cd591568791556d246d901d6aaf9935ab02c3f9a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/c5e2232033b507a07f72180dc56d37e1872ee7be",
"reference": "c5e2232033b507a07f72180dc56d37e1872ee7be",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd591568791556d246d901d6aaf9935ab02c3f9a",
"reference": "cd591568791556d246d901d6aaf9935ab02c3f9a",
"shasum": ""
},
"require": {
@@ -3310,7 +3310,7 @@
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/database": "1.*"
"utopia-php/database": "2.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3338,9 +3338,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/1.0.0"
"source": "https://github.com/utopia-php/abuse/tree/1.0.1"
},
"time": "2025-08-13T09:12:54+00:00"
"time": "2025-09-04T12:46:54+00:00"
},
{
"name": "utopia-php/analytics",
@@ -3390,21 +3390,21 @@
},
{
"name": "utopia-php/audit",
"version": "1.0.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb"
"reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/c0ed75f4d068f1f6c2e7149a909490d4214e72bb",
"reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/5ef26d6a2ab2db7bb86288a1a6ef970307b46f22",
"reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "1.*"
"utopia-php/database": "2.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3431,9 +3431,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/1.0.0"
"source": "https://github.com/utopia-php/audit/tree/1.0.1"
},
"time": "2025-08-13T09:09:00+00:00"
"time": "2025-09-04T12:46:43+00:00"
},
{
"name": "utopia-php/auth",
@@ -3690,16 +3690,16 @@
},
{
"name": "utopia-php/database",
"version": "1.4.9",
"version": "2.3.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "066e2bda7b728bb843776db3640737d7350ba035"
"reference": "35c978ddd20b649d119296094686d3cc9fcf161f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/066e2bda7b728bb843776db3640737d7350ba035",
"reference": "066e2bda7b728bb843776db3640737d7350ba035",
"url": "https://api.github.com/repos/utopia-php/database/zipball/35c978ddd20b649d119296094686d3cc9fcf161f",
"reference": "35c978ddd20b649d119296094686d3cc9fcf161f",
"shasum": ""
},
"require": {
@@ -3740,9 +3740,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/1.4.9"
"source": "https://github.com/utopia-php/database/tree/2.3.2"
},
"time": "2025-09-16T13:31:52+00:00"
"time": "2025-10-07T03:09:32+00:00"
},
{
"name": "utopia-php/detector",
@@ -3847,16 +3847,16 @@
},
{
"name": "utopia-php/domains",
"version": "0.8.0",
"version": "0.8.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/domains.git",
"reference": "650463d2a1525273eb03223c48da9fb1a768bbf7"
"reference": "caa294dcebd05c8af876c8afef3e992faccdf645"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/domains/zipball/650463d2a1525273eb03223c48da9fb1a768bbf7",
"reference": "650463d2a1525273eb03223c48da9fb1a768bbf7",
"url": "https://api.github.com/repos/utopia-php/domains/zipball/caa294dcebd05c8af876c8afef3e992faccdf645",
"reference": "caa294dcebd05c8af876c8afef3e992faccdf645",
"shasum": ""
},
"require": {
@@ -3902,9 +3902,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/domains/issues",
"source": "https://github.com/utopia-php/domains/tree/0.8.0"
"source": "https://github.com/utopia-php/domains/tree/0.8.2"
},
"time": "2025-05-16T10:03:59+00:00"
"time": "2025-10-06T09:56:54+00:00"
},
{
"name": "utopia-php/dsn",
@@ -3994,16 +3994,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.27",
"version": "0.33.28",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37"
"reference": "5aaa94d406577b0059ad28c78022606890dc6de0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/d9d10a895e85c8c7675220347cc6109db9d3bd37",
"reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37",
"url": "https://api.github.com/repos/utopia-php/http/zipball/5aaa94d406577b0059ad28c78022606890dc6de0",
"reference": "5aaa94d406577b0059ad28c78022606890dc6de0",
"shasum": ""
},
"require": {
@@ -4035,9 +4035,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.27"
"source": "https://github.com/utopia-php/http/tree/0.33.28"
},
"time": "2025-09-07T18:40:53+00:00"
"time": "2025-09-25T10:44:24+00:00"
},
{
"name": "utopia-php/image",
@@ -4242,16 +4242,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.1.1",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4"
"reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/c42935a6a4ee3701c68d24244e82ecb39e945ec4",
"reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/6fb6f8f032cd34c3c65728a55d494adeac2ff038",
"reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038",
"shasum": ""
},
"require": {
@@ -4259,7 +4259,7 @@
"ext-curl": "*",
"ext-openssl": "*",
"php": ">=8.1",
"utopia-php/database": "1.*",
"utopia-php/database": "2.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",
"utopia-php/storage": "0.18.*"
@@ -4292,9 +4292,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.1.1"
"source": "https://github.com/utopia-php/migration/tree/1.1.0"
},
"time": "2025-09-10T06:17:20+00:00"
"time": "2025-09-10T05:45:30+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4621,16 +4621,16 @@
},
{
"name": "utopia-php/storage",
"version": "0.18.13",
"version": "0.18.14",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "3d8ce53ae042173bf230445e996056c5f65ded22"
"reference": "4f14ec952c6f4006dd0613e55bbf7631814fbc00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/3d8ce53ae042173bf230445e996056c5f65ded22",
"reference": "3d8ce53ae042173bf230445e996056c5f65ded22",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/4f14ec952c6f4006dd0613e55bbf7631814fbc00",
"reference": "4f14ec952c6f4006dd0613e55bbf7631814fbc00",
"shasum": ""
},
"require": {
@@ -4673,9 +4673,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.18.13"
"source": "https://github.com/utopia-php/storage/tree/0.18.14"
},
"time": "2025-05-26T13:10:35+00:00"
"time": "2025-10-07T10:21:47+00:00"
},
{
"name": "utopia-php/swoole",
@@ -5059,16 +5059,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.3.4",
"version": "1.4.3",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac"
"reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac",
"reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3",
"reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3",
"shasum": ""
},
"require": {
@@ -5104,9 +5104,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.3.4"
"source": "https://github.com/appwrite/sdk-generator/tree/1.4.3"
},
"time": "2025-09-08T11:56:04+00:00"
"time": "2025-10-01T06:25:19+00:00"
},
{
"name": "doctrine/annotations",
@@ -5182,6 +5182,7 @@
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/2.0.2"
},
"abandoned": true,
"time": "2024-09-05T10:17:24+00:00"
},
{
@@ -5333,16 +5334,16 @@
},
{
"name": "laravel/pint",
"version": "v1.24.0",
"version": "v1.25.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a"
"reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a",
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a",
"url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9",
"reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9",
"shasum": ""
},
"require": {
@@ -5353,9 +5354,9 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.82.2",
"illuminate/view": "^11.45.1",
"larastan/larastan": "^3.5.0",
"friendsofphp/php-cs-fixer": "^3.87.2",
"illuminate/view": "^11.46.0",
"larastan/larastan": "^3.7.1",
"laravel-zero/framework": "^11.45.0",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3.1",
@@ -5366,9 +5367,6 @@
],
"type": "project",
"autoload": {
"files": [
"overrides/Runner/Parallel/ProcessFactory.php"
],
"psr-4": {
"App\\": "app/",
"Database\\Seeders\\": "database/seeders/",
@@ -5398,7 +5396,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-07-10T18:09:32+00:00"
"time": "2025-09-19T02:57:12+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -6288,16 +6286,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.27",
"version": "9.6.29",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "0a9aa4440b6a9528cf360071502628d717af3e0a"
"reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0a9aa4440b6a9528cf360071502628d717af3e0a",
"reference": "0a9aa4440b6a9528cf360071502628d717af3e0a",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3",
"reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3",
"shasum": ""
},
"require": {
@@ -6322,7 +6320,7 @@
"sebastian/comparator": "^4.0.9",
"sebastian/diff": "^4.0.6",
"sebastian/environment": "^5.1.5",
"sebastian/exporter": "^4.0.6",
"sebastian/exporter": "^4.0.8",
"sebastian/global-state": "^5.0.8",
"sebastian/object-enumerator": "^4.0.4",
"sebastian/resource-operations": "^3.0.4",
@@ -6371,7 +6369,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.27"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29"
},
"funding": [
{
@@ -6395,7 +6393,7 @@
"type": "tidelift"
}
],
"time": "2025-09-14T06:18:03+00:00"
"time": "2025-09-24T06:29:11+00:00"
},
{
"name": "psr/cache",
@@ -6887,16 +6885,16 @@
},
{
"name": "sebastian/exporter",
"version": "4.0.6",
"version": "4.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "78c00df8f170e02473b682df15bfcdacc3d32d72"
"reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72",
"reference": "78c00df8f170e02473b682df15bfcdacc3d32d72",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c",
"reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c",
"shasum": ""
},
"require": {
@@ -6952,15 +6950,27 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6"
"source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
"type": "tidelift"
}
],
"time": "2024-03-02T06:33:00+00:00"
"time": "2025-09-24T06:03:27+00:00"
},
{
"name": "sebastian/global-state",
@@ -7543,16 +7553,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.3",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
"reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db",
"reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db",
"shasum": ""
},
"require": {
@@ -7617,7 +7627,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.3"
"source": "https://github.com/symfony/console/tree/v7.3.4"
},
"funding": [
{
@@ -7637,7 +7647,7 @@
"type": "tidelift"
}
],
"time": "2025-08-25T06:35:40+00:00"
"time": "2025-09-22T15:31:00+00:00"
},
{
"name": "symfony/filesystem",
@@ -8180,16 +8190,16 @@
},
{
"name": "symfony/process",
"version": "v7.3.3",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
"reference": "f24f8f316367b30810810d4eb30c543d7003ff3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
"url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b",
"reference": "f24f8f316367b30810810d4eb30c543d7003ff3b",
"shasum": ""
},
"require": {
@@ -8221,7 +8231,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.3.3"
"source": "https://github.com/symfony/process/tree/v7.3.4"
},
"funding": [
{
@@ -8241,20 +8251,20 @@
"type": "tidelift"
}
],
"time": "2025-08-18T09:42:54+00:00"
"time": "2025-09-11T10:12:26+00:00"
},
{
"name": "symfony/string",
"version": "v7.3.3",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
"reference": "f96476035142921000338bad71e5247fbc138872"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872",
"reference": "f96476035142921000338bad71e5247fbc138872",
"shasum": ""
},
"require": {
@@ -8269,7 +8279,6 @@
},
"require-dev": {
"symfony/emoji": "^7.1",
"symfony/error-handler": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0",
"symfony/intl": "^6.4|^7.0",
"symfony/translation-contracts": "^2.5|^3.0",
@@ -8312,7 +8321,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.3"
"source": "https://github.com/symfony/string/tree/v7.3.4"
},
"funding": [
{
@@ -8332,7 +8341,7 @@
"type": "tidelift"
}
],
"time": "2025-08-25T06:35:40+00:00"
"time": "2025-09-11T14:36:48+00:00"
},
{
"name": "textalk/websocket",
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Account account = new Account(client);
account.createEmailVerification(
"https://example.com", // url
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -0,0 +1,23 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Account account = new Account(client);
account.updateEmailVerification(
"<USER_ID>", // userId
"<SECRET>", // secret
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -20,6 +20,7 @@ databases.createDocument(
"isAdmin" to false
), // data
listOf("read("any")"), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,33 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
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)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.createTransaction(
60, // ttl (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -15,6 +15,7 @@ databases.decrementDocumentAttribute(
"", // attribute
0, // value (optional)
0, // min (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,6 +12,7 @@ databases.deleteDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.deleteTransaction(
"<TRANSACTION_ID>", // transactionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -13,6 +13,7 @@ databases.getDocument(
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
listOf(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.getTransaction(
"<TRANSACTION_ID>", // transactionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -15,6 +15,7 @@ databases.incrementDocumentAttribute(
"", // attribute
0, // value (optional)
0, // max (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,6 +12,7 @@ databases.listDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.listTransactions(
listOf(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -14,6 +14,7 @@ databases.updateDocument(
"<DOCUMENT_ID>", // documentId
mapOf( "a" to "b" ), // data (optional)
listOf("read("any")"), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,24 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.updateTransaction(
"<TRANSACTION_ID>", // transactionId
false, // commit (optional)
false, // rollback (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -14,6 +14,7 @@ databases.upsertDocument(
"<DOCUMENT_ID>", // documentId
mapOf( "a" to "b" ), // data
listOf("read("any")"), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,33 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
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)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -20,6 +20,7 @@ tablesDB.createRow(
"isAdmin" to false
), // data
listOf("read("any")"), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
TablesDB tablesDB = new TablesDB(client);
tablesDB.createTransaction(
60, // ttl (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -15,6 +15,7 @@ tablesDB.decrementRowColumn(
"", // column
0, // value (optional)
0, // min (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,6 +12,7 @@ tablesDB.deleteRow(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
"<ROW_ID>", // rowId
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
TablesDB tablesDB = new TablesDB(client);
tablesDB.deleteTransaction(
"<TRANSACTION_ID>", // transactionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -13,6 +13,7 @@ tablesDB.getRow(
"<TABLE_ID>", // tableId
"<ROW_ID>", // rowId
listOf(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
TablesDB tablesDB = new TablesDB(client);
tablesDB.getTransaction(
"<TRANSACTION_ID>", // transactionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -15,6 +15,7 @@ tablesDB.incrementRowColumn(
"", // column
0, // value (optional)
0, // max (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,6 +12,7 @@ tablesDB.listRows(
"<DATABASE_ID>", // databaseId
"<TABLE_ID>", // tableId
listOf(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
TablesDB tablesDB = new TablesDB(client);
tablesDB.listTransactions(
listOf(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -14,6 +14,7 @@ tablesDB.updateRow(
"<ROW_ID>", // rowId
mapOf( "a" to "b" ), // data (optional)
listOf("read("any")"), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,24 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.TablesDB;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
TablesDB tablesDB = new TablesDB(client);
tablesDB.updateTransaction(
"<TRANSACTION_ID>", // transactionId
false, // commit (optional)
false, // rollback (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -14,6 +14,7 @@ tablesDB.upsertRow(
"<ROW_ID>", // rowId
mapOf( "a" to "b" ), // data (optional)
listOf("read("any")"), // permissions (optional)
"<TRANSACTION_ID>", // transactionId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Account
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val account = Account(client)
val result = account.createEmailVerification(
url = "https://example.com",
)
@@ -0,0 +1,14 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Account
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val account = Account(client)
val result = account.updateEmailVerification(
userId = "<USER_ID>",
secret = "<SECRET>",
)
@@ -20,4 +20,5 @@ val result = databases.createDocument(
"isAdmin" to false
),
permissions = listOf("read("any")"), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,24 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
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)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.createTransaction(
ttl = 60, // (optional)
)
@@ -15,4 +15,5 @@ val result = databases.decrementDocumentAttribute(
attribute = "",
value = 0, // (optional)
min = 0, // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -12,4 +12,5 @@ val result = databases.deleteDocument(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
documentId = "<DOCUMENT_ID>",
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.deleteTransaction(
transactionId = "<TRANSACTION_ID>",
)
@@ -13,4 +13,5 @@ val result = databases.getDocument(
collectionId = "<COLLECTION_ID>",
documentId = "<DOCUMENT_ID>",
queries = listOf(), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.getTransaction(
transactionId = "<TRANSACTION_ID>",
)
@@ -15,4 +15,5 @@ val result = databases.incrementDocumentAttribute(
attribute = "",
value = 0, // (optional)
max = 0, // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -12,4 +12,5 @@ val result = databases.listDocuments(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
queries = listOf(), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.listTransactions(
queries = listOf(), // (optional)
)
@@ -14,4 +14,5 @@ val result = databases.updateDocument(
documentId = "<DOCUMENT_ID>",
data = mapOf( "a" to "b" ), // (optional)
permissions = listOf("read("any")"), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,15 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.updateTransaction(
transactionId = "<TRANSACTION_ID>",
commit = false, // (optional)
rollback = false, // (optional)
)
@@ -14,4 +14,5 @@ val result = databases.upsertDocument(
documentId = "<DOCUMENT_ID>",
data = mapOf( "a" to "b" ),
permissions = listOf("read("any")"), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,24 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.TablesDB
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
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)
)
@@ -20,4 +20,5 @@ val result = tablesDB.createRow(
"isAdmin" to false
),
permissions = listOf("read("any")"), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.TablesDB
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val tablesDB = TablesDB(client)
val result = tablesDB.createTransaction(
ttl = 60, // (optional)
)
@@ -15,4 +15,5 @@ val result = tablesDB.decrementRowColumn(
column = "",
value = 0, // (optional)
min = 0, // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -12,4 +12,5 @@ val result = tablesDB.deleteRow(
databaseId = "<DATABASE_ID>",
tableId = "<TABLE_ID>",
rowId = "<ROW_ID>",
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.TablesDB
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val tablesDB = TablesDB(client)
val result = tablesDB.deleteTransaction(
transactionId = "<TRANSACTION_ID>",
)
@@ -13,4 +13,5 @@ val result = tablesDB.getRow(
tableId = "<TABLE_ID>",
rowId = "<ROW_ID>",
queries = listOf(), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.TablesDB
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val tablesDB = TablesDB(client)
val result = tablesDB.getTransaction(
transactionId = "<TRANSACTION_ID>",
)
@@ -15,4 +15,5 @@ val result = tablesDB.incrementRowColumn(
column = "",
value = 0, // (optional)
max = 0, // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -12,4 +12,5 @@ val result = tablesDB.listRows(
databaseId = "<DATABASE_ID>",
tableId = "<TABLE_ID>",
queries = listOf(), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.TablesDB
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val tablesDB = TablesDB(client)
val result = tablesDB.listTransactions(
queries = listOf(), // (optional)
)
@@ -14,4 +14,5 @@ val result = tablesDB.updateRow(
rowId = "<ROW_ID>",
data = mapOf( "a" to "b" ), // (optional)
permissions = listOf("read("any")"), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,15 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.TablesDB
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val tablesDB = TablesDB(client)
val result = tablesDB.updateTransaction(
transactionId = "<TRANSACTION_ID>",
commit = false, // (optional)
rollback = false, // (optional)
)
@@ -14,4 +14,5 @@ val result = tablesDB.upsertRow(
rowId = "<ROW_ID>",
data = mapOf( "a" to "b" ), // (optional)
permissions = listOf("read("any")"), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
)
@@ -0,0 +1,12 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let account = Account(client)
let token = try await account.createEmailVerification(
url: "https://example.com"
)
@@ -0,0 +1,13 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let account = Account(client)
let token = try await account.updateEmailVerification(
userId: "<USER_ID>",
secret: "<SECRET>"
)
@@ -17,6 +17,7 @@ let document = try await databases.createDocument(
"age": 30,
"isAdmin": false
],
permissions: ["read("any")"] // optional
permissions: ["read("any")"], // optional
transactionId: "<TRANSACTION_ID>" // optional
)
@@ -0,0 +1,23 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let databases = Databases(client)
let transaction = try await databases.createOperations(
transactionId: "<TRANSACTION_ID>",
operations: [
{
"action": "create",
"databaseId": "<DATABASE_ID>",
"collectionId": "<COLLECTION_ID>",
"documentId": "<DOCUMENT_ID>",
"data": {
"name": "Walter O'Brien"
}
}
] // optional
)
@@ -0,0 +1,12 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let databases = Databases(client)
let transaction = try await databases.createTransaction(
ttl: 60 // optional
)
@@ -12,6 +12,7 @@ let document = try await databases.decrementDocumentAttribute(
documentId: "<DOCUMENT_ID>",
attribute: "",
value: 0, // optional
min: 0 // optional
min: 0, // optional
transactionId: "<TRANSACTION_ID>" // optional
)

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