Merge branch '1.5.x' into fix-templates-copy-hidden-files
This commit is contained in:
commit
34d2dde51d
28 changed files with 314 additions and 243 deletions
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -141,7 +141,7 @@ jobs:
|
|||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
sleep 25
|
||||
|
||||
- name: Run ${{matrix.service}} Tests
|
||||
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,4 +1,4 @@
|
|||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = chore-update-sdk
|
||||
branch = 1.5.x
|
||||
|
|
11
Dockerfile
11
Dockerfile
|
@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
|
|||
RUN npm ci
|
||||
RUN npm run build
|
||||
|
||||
FROM appwrite/base:0.8.0 as final
|
||||
FROM appwrite/base:0.9.0 as final
|
||||
|
||||
LABEL maintainer="team@appwrite.io"
|
||||
|
||||
|
@ -133,16 +133,9 @@ RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
|||
|
||||
# Enable Extensions
|
||||
RUN if [ "$DEBUG" == "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
|
||||
RUN if [ "$DEBUG" == "true" ]; then echo "opcache.enable=0" >> /usr/local/etc/php/conf.d/appwrite.ini; fi
|
||||
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
|
||||
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so; fi
|
||||
RUN echo "opcache.preload_user=www-data" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.preload=/usr/src/code/app/preload.php" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "default_socket_timeout=-1" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.jit=1235" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD [ "php", "app/http.php", "-dopcache.preload=opcache.preload=/usr/src/code/app/preload.php" ]
|
||||
CMD [ "php", "app/http.php" ]
|
|
@ -6,10 +6,12 @@ return [
|
|||
'magicSession',
|
||||
'recovery',
|
||||
'invitation',
|
||||
'mfaChallenge'
|
||||
],
|
||||
'sms' => [
|
||||
'verification',
|
||||
'login',
|
||||
'invitation'
|
||||
'invitation',
|
||||
'mfaChallenge'
|
||||
]
|
||||
];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<p>{{hello}}</p>
|
||||
<p>{{hello}},</p>
|
||||
|
||||
<p>{{optionButton}}</p>
|
||||
|
||||
|
|
16
app/config/locale/templates/email-mfa-challenge.tpl
Normal file
16
app/config/locale/templates/email-mfa-challenge.tpl
Normal file
|
@ -0,0 +1,16 @@
|
|||
<p>{{hello}},</p>
|
||||
|
||||
<p>{{description}}</p>
|
||||
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>{{clientInfo}}</p>
|
||||
|
||||
<p style="margin-bottom: 0px;">{{thanks}}</p>
|
||||
<p style="margin-top: 0px;">{{signature}}</p>
|
|
@ -1,4 +1,4 @@
|
|||
<p>{{hello}}</p>
|
||||
<p>{{hello}},</p>
|
||||
|
||||
<p>{{description}}</p>
|
||||
|
||||
|
|
|
@ -4,36 +4,42 @@
|
|||
"settings.direction": "ltr",
|
||||
"emails.sender": "%s Team",
|
||||
"emails.verification.subject": "Account Verification",
|
||||
"emails.verification.hello": "Hey {{user}}",
|
||||
"emails.verification.body": "Follow this link to verify your email address.",
|
||||
"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 didn’t ask to verify this address, you can ignore this message.",
|
||||
"emails.verification.thanks": "Thanks",
|
||||
"emails.verification.signature": "{{project}} team",
|
||||
"emails.magicSession.subject": "{{project}} Login",
|
||||
"emails.magicSession.hello": "Hello,",
|
||||
"emails.magicSession.optionButton": "Click the button below to securely sign in to your {{project}} account. This link will expire in 1 hour.",
|
||||
"emails.magicSession.hello": "Hello {{user}}",
|
||||
"emails.magicSession.optionButton": "Click the button below to securely sign in to your {{b}}{{project}}{{/b}} account. This link will expire in 1 hour.",
|
||||
"emails.magicSession.buttonText": "Sign in to {{project}}",
|
||||
"emails.magicSession.optionUrl": "If you are unable to sign in using the button above, please visit the following link:",
|
||||
"emails.magicSession.clientInfo": "This sign in was requested using {{agentClient}} on {{agentDevice}} {{agentOs}}. If you didn't request the sign in, you can safely ignore this email.",
|
||||
"emails.magicSession.securityPhrase": "Security phrase for this email is {{phrase}}. You can trust this email if this phrase matches the phrase shown during sign in.",
|
||||
"emails.magicSession.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.",
|
||||
"emails.magicSession.securityPhrase": "Security phrase for this email is {{b}}{{phrase}}{{/b}}. You can trust this email if this phrase matches the phrase shown during sign in.",
|
||||
"emails.magicSession.thanks": "Thanks,",
|
||||
"emails.magicSession.signature": "{{project}} team",
|
||||
"emails.otpSession.subject": "OTP for {{project}} Login",
|
||||
"emails.otpSession.hello": "Hello,",
|
||||
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{project}} account. This code will expire in 15 minutes.",
|
||||
"emails.otpSession.clientInfo": "This sign in was requested using {{agentClient}} on {{agentDevice}} {{agentOs}}. If you didn't request the sign in, you can safely ignore this email.",
|
||||
"emails.otpSession.securityPhrase": "Security phrase for this email is {{phrase}}. You can trust this email if this phrase matches the phrase shown during sign in.",
|
||||
"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.",
|
||||
"emails.otpSession.securityPhrase": "Security phrase for this email is {{b}}{{phrase}}{{/b}}. You can trust this email if this phrase matches the phrase shown during sign in.",
|
||||
"emails.otpSession.thanks": "Thanks,",
|
||||
"emails.otpSession.signature": "{{project}} team",
|
||||
"emails.mfaChallenge.subject": "Verification Code for {{project}}",
|
||||
"emails.mfaChallenge.hello": "Hello {{user}}",
|
||||
"emails.mfaChallenge.description": "Enter the following verification code to verify your email and activate 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.hello": "Hello {{user}}",
|
||||
"emails.recovery.body": "Follow this link to reset your {{project}} password.",
|
||||
"emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.",
|
||||
"emails.recovery.footer": "If you didn’t ask to reset your password, you can ignore this message.",
|
||||
"emails.recovery.thanks": "Thanks",
|
||||
"emails.recovery.signature": "{{project}} team",
|
||||
"emails.invitation.subject": "Invitation to %s Team at %s",
|
||||
"emails.invitation.hello": "Hello",
|
||||
"emails.invitation.body": "This mail was sent to you because {{owner}} wanted to invite you to become a member of the {{team}} team at {{project}}.",
|
||||
"emails.invitation.hello": "Hello {{user}}",
|
||||
"emails.invitation.body": "This mail was sent to you because {{b}}{{owner}}{{/b}} wanted to invite you to become a member of the {{b}}{{team}}{{/b}} team at {{b}}{{project}}{{/b}}.",
|
||||
"emails.invitation.footer": "If you are not interested, you can ignore this message.",
|
||||
"emails.invitation.thanks": "Thanks",
|
||||
"emails.invitation.signature": "{{project}} team",
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 44edd461c6036cb462047c1424b80f0903cdc15e
|
||||
Subproject commit c72ba12e479b0d3d9b3f4e48e01625c12a38fd7f
|
|
@ -1259,15 +1259,16 @@ App::post('/v1/account/tokens/magic-url')
|
|||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
/* {{user}}, {{team}}, {{redirect}} and {{project}} are required in default and custom templates */
|
||||
'user' => '',
|
||||
'team' => '',
|
||||
// {{user}}, {{redirect}} and {{project}} are required in default and custom templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'redirect' => $url,
|
||||
'agentDevice' => '<strong>' . ( $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN') . '</strong>',
|
||||
'agentClient' => '<strong>' . ($agentClient['clientName'] ?? 'UNKNOWN') . '</strong>',
|
||||
'agentOs' => '<strong>' . ($agentOs['osName'] ?? 'UNKNOWN') . '</strong>',
|
||||
'phrase' => '<strong>' . (!empty($phrase) ? $phrase : '') . '</strong>'
|
||||
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
|
||||
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
|
||||
'agentOs' => $agentOs['osName'] ?? 'UNKNOWN',
|
||||
'phrase' => !empty($phrase) ? $phrase : '',
|
||||
// TODO: remove unnecessary team variable from this email
|
||||
'team' => '',
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
|
@ -1487,15 +1488,16 @@ App::post('/v1/account/tokens/email')
|
|||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
/* {{user}} ,{{team}}, {{project}} and {{otp}} are required in the templates */
|
||||
'user' => '',
|
||||
'team' => '',
|
||||
// {{user}}, {{project}} and {{otp}} are required in the templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'otp' => $tokenSecret,
|
||||
'agentDevice' => '<strong>' . ( $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN') . '</strong>',
|
||||
'agentClient' => '<strong>' . ($agentClient['clientName'] ?? 'UNKNOWN') . '</strong>',
|
||||
'agentOs' => '<strong>' . ($agentOs['osName'] ?? 'UNKNOWN') . '</strong>',
|
||||
'phrase' => '<strong>' . (!empty($phrase) ? $phrase : '') . '</strong>'
|
||||
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
|
||||
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
|
||||
'agentOs' => $agentOs['osName'] ?? 'UNKNOWN',
|
||||
'phrase' => !empty($phrase) ? $phrase : '',
|
||||
// TODO: remove unnecessary team variable from this email
|
||||
'team' => '',
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
|
@ -2953,11 +2955,12 @@ App::post('/v1/account/recovery')
|
|||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
/* {{user}}, {{team}}, {{redirect}} and {{project}} are required in default and custom templates */
|
||||
// {{user}}, {{redirect}} and {{project}} are required in default and custom templates
|
||||
'user' => $profile->getAttribute('name'),
|
||||
'team' => '',
|
||||
'redirect' => $url,
|
||||
'project' => $projectName
|
||||
'project' => $projectName,
|
||||
// TODO: remove unnecessary team variable from this email
|
||||
'team' => ''
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
|
@ -3200,11 +3203,12 @@ App::post('/v1/account/verification')
|
|||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
/* {{user}}, {{team}}, {{redirect}} and {{project}} are required in default and custom templates */
|
||||
// {{user}}, {{redirect}} and {{project}} are required in default and custom templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'team' => '',
|
||||
'redirect' => $url,
|
||||
'project' => $projectName
|
||||
'project' => $projectName,
|
||||
// TODO: remove unnecessary team variable from this email
|
||||
'team' => '',
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
|
@ -3696,7 +3700,7 @@ App::post('/v1/account/mfa/challenge')
|
|||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'create2FAChallenge')
|
||||
->label('sdk.method', 'createChallenge')
|
||||
->label('sdk.description', '/docs/references/account/create-2fa-challenge.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
|
@ -3707,11 +3711,13 @@ App::post('/v1/account/mfa/challenge')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('locale')
|
||||
->inject('project')
|
||||
->inject('request')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMessaging')
|
||||
->inject('queueForMails')
|
||||
->inject('locale')
|
||||
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, Locale $locale) {
|
||||
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails) {
|
||||
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$code = Auth::codeGenerator();
|
||||
|
@ -3743,6 +3749,22 @@ App::post('/v1/account/mfa/challenge')
|
|||
throw new Exception(Exception::USER_PHONE_NOT_VERIFIED);
|
||||
}
|
||||
|
||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
|
||||
|
||||
$customTemplate = $project->getAttribute('templates', [])['sms.mfaChallenge-' . $locale->default] ?? [];
|
||||
if (!empty($customTemplate)) {
|
||||
$message = $customTemplate['message'] ?? $message;
|
||||
}
|
||||
|
||||
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
|
||||
$messageContent
|
||||
->setParam('{{project}}', $project->getAttribute('name'))
|
||||
->setParam('{{secret}}', $code);
|
||||
$messageContent = \strip_tags($messageContent->render());
|
||||
$message = $message->setParam('{{token}}', $messageContent);
|
||||
|
||||
$message = $message->render();
|
||||
|
||||
$queueForMessaging
|
||||
->setType(MESSAGE_SEND_TYPE_INTERNAL)
|
||||
->setMessage(new Document([
|
||||
|
@ -3751,7 +3773,8 @@ App::post('/v1/account/mfa/challenge')
|
|||
'content' => $code,
|
||||
],
|
||||
]))
|
||||
->setRecipients([$user->getAttribute('phone')]);
|
||||
->setRecipients([$user->getAttribute('phone')])
|
||||
->setProviderType(MESSAGE_TYPE_SMS);
|
||||
break;
|
||||
case 'email':
|
||||
if (empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
|
@ -3764,9 +3787,85 @@ App::post('/v1/account/mfa/challenge')
|
|||
throw new Exception(Exception::USER_EMAIL_NOT_VERIFIED);
|
||||
}
|
||||
|
||||
$subject = $locale->getText("emails.mfaChallenge.subject");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? [];
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$agentOs = $detector->getOS();
|
||||
$agentClient = $detector->getClient();
|
||||
$agentDevice = $detector->getDevice();
|
||||
|
||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-mfa-challenge.tpl');
|
||||
$message
|
||||
->setParam('{{hello}}', $locale->getText("emails.mfaChallenge.hello"))
|
||||
->setParam('{{description}}', $locale->getText("emails.mfaChallenge.description"))
|
||||
->setParam('{{clientInfo}}', $locale->getText("emails.mfaChallenge.clientInfo"))
|
||||
->setParam('{{thanks}}', $locale->getText("emails.mfaChallenge.thanks"))
|
||||
->setParam('{{signature}}', $locale->getText("emails.mfaChallenge.signature"));
|
||||
|
||||
$body = $message->render();
|
||||
|
||||
$smtp = $project->getAttribute('smtp', []);
|
||||
$smtpEnabled = $smtp['enabled'] ?? false;
|
||||
|
||||
$senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
$senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
|
||||
$replyTo = "";
|
||||
|
||||
if ($smtpEnabled) {
|
||||
if (!empty($smtp['senderEmail'])) {
|
||||
$senderEmail = $smtp['senderEmail'];
|
||||
}
|
||||
if (!empty($smtp['senderName'])) {
|
||||
$senderName = $smtp['senderName'];
|
||||
}
|
||||
if (!empty($smtp['replyTo'])) {
|
||||
$replyTo = $smtp['replyTo'];
|
||||
}
|
||||
|
||||
$queueForMails
|
||||
->setSmtpHost($smtp['host'] ?? '')
|
||||
->setSmtpPort($smtp['port'] ?? '')
|
||||
->setSmtpUsername($smtp['username'] ?? '')
|
||||
->setSmtpPassword($smtp['password'] ?? '')
|
||||
->setSmtpSecure($smtp['secure'] ?? '');
|
||||
|
||||
if (!empty($customTemplate)) {
|
||||
if (!empty($customTemplate['senderEmail'])) {
|
||||
$senderEmail = $customTemplate['senderEmail'];
|
||||
}
|
||||
if (!empty($customTemplate['senderName'])) {
|
||||
$senderName = $customTemplate['senderName'];
|
||||
}
|
||||
if (!empty($customTemplate['replyTo'])) {
|
||||
$replyTo = $customTemplate['replyTo'];
|
||||
}
|
||||
|
||||
$body = $customTemplate['message'] ?? '';
|
||||
$subject = $customTemplate['subject'] ?? $subject;
|
||||
}
|
||||
|
||||
$queueForMails
|
||||
->setSmtpReplyTo($replyTo)
|
||||
->setSmtpSenderEmail($senderEmail)
|
||||
->setSmtpSenderName($senderName);
|
||||
}
|
||||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
// {{user}}, {{project}} and {{otp}} are required in the templates
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'otp' => $code,
|
||||
'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN',
|
||||
'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN',
|
||||
'agentOs' => $agentOs['osName'] ?? 'UNKNOWN'
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
->setSubject("{$code} is your 6-digit code")
|
||||
->setBody($code)
|
||||
->setSubject($subject)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($user->getAttribute('email'))
|
||||
->trigger();
|
||||
break;
|
||||
|
|
|
@ -766,11 +766,12 @@ App::post('/v1/messaging/providers/apns')
|
|||
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true)
|
||||
->param('teamId', '', new Text(0), 'APNS team ID.', true)
|
||||
->param('bundleId', '', new Text(0), 'APNS bundle ID.', true)
|
||||
->param('sandbox', false, new Boolean(), 'Use APNS sandbox environment.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, string $authKey, string $authKeyId, string $teamId, string $bundleId, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, string $authKey, string $authKeyId, string $teamId, string $bundleId, bool $sandbox, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
|
||||
|
||||
$credentials = [];
|
||||
|
@ -803,6 +804,10 @@ App::post('/v1/messaging/providers/apns')
|
|||
$enabled = false;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'sandbox' => $sandbox
|
||||
];
|
||||
|
||||
$provider = new Document([
|
||||
'$id' => $providerId,
|
||||
'name' => $name,
|
||||
|
@ -810,6 +815,7 @@ App::post('/v1/messaging/providers/apns')
|
|||
'type' => MESSAGE_TYPE_PUSH,
|
||||
'enabled' => $enabled,
|
||||
'credentials' => $credentials,
|
||||
'options' => $options
|
||||
]);
|
||||
|
||||
try {
|
||||
|
@ -1808,10 +1814,11 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
|||
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true)
|
||||
->param('teamId', '', new Text(0), 'APNS team ID.', true)
|
||||
->param('bundleId', '', new Text(0), 'APNS bundle ID.', true)
|
||||
->param('sandbox', null, new Boolean(), 'Use APNS sandbox environment.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $authKey, string $authKeyId, string $teamId, string $bundleId, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $authKey, string $authKeyId, string $teamId, string $bundleId, ?bool $sandbox, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1847,6 +1854,14 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
|||
|
||||
$provider->setAttribute('credentials', $credentials);
|
||||
|
||||
$options = $provider->getAttribute('options');
|
||||
|
||||
if (!\is_null($sandbox)) {
|
||||
$options['sandbox'] = $sandbox;
|
||||
}
|
||||
|
||||
$provider->setAttribute('options', $options);
|
||||
|
||||
if (!\is_null($enabled)) {
|
||||
if ($enabled) {
|
||||
if (
|
||||
|
@ -2133,21 +2148,26 @@ App::patch('/v1/messaging/topics/:topicId')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TOPIC)
|
||||
->param('topicId', '', new UID(), 'Topic ID.')
|
||||
->param('name', '', new Text(128), 'Topic Name.', true)
|
||||
->param('name', null, new Text(128), 'Topic Name.', true)
|
||||
->param('subscribe', null, new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $topicId, string $name, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $topicId, ?string $name, ?array $subscribe, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$topic = $dbForProject->getDocument('topics', $topicId);
|
||||
|
||||
if ($topic->isEmpty()) {
|
||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($name)) {
|
||||
if (!\is_null($name)) {
|
||||
$topic->setAttribute('name', $name);
|
||||
}
|
||||
|
||||
if (!\is_null($subscribe)) {
|
||||
$topic->setAttribute('subscribe', $subscribe);
|
||||
}
|
||||
|
||||
$topic = $dbForProject->updateDocument('topics', $topicId, $topic);
|
||||
|
||||
$queueForEvents
|
||||
|
|
|
@ -513,7 +513,7 @@ App::post('/v1/users/:userId/targets')
|
|||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'providerId' => $providerId ?? null,
|
||||
'providerInternalId' => $provider->getInternalId() ?? null,
|
||||
'providerInternalId' => $provider->isEmpty() ? null : $provider->getInternalId(),
|
||||
'providerType' => $providerType,
|
||||
'userId' => $userId,
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
|
@ -1662,7 +1662,7 @@ App::post('/v1/users/:userId/sessions')
|
|||
->inject('queueForEvents')
|
||||
->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
if ($user === false || $user->isEmpty()) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
@ -1731,7 +1731,7 @@ App::post('/v1/users/:userId/tokens')
|
|||
->action(function (string $userId, int $length, int $expire, Request $request, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user === false || $user->isEmpty()) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ App::get('/console/*')
|
|||
->alias('auth/*')
|
||||
->alias('/invite')
|
||||
->alias('/login')
|
||||
->alias('/mfa')
|
||||
->alias('/card/*')
|
||||
->alias('/recover')
|
||||
->alias('/register/*')
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* Inializes both Appwrite API entry point, queue workers, and CLI tasks.
|
||||
* Set configuration, framework resources, app constants
|
||||
*
|
||||
*/
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
}
|
||||
|
||||
use Utopia\Preloader\Preloader;
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
$preloader = new Preloader();
|
||||
|
||||
foreach (
|
||||
[
|
||||
realpath(__DIR__ . '/../vendor/composer'),
|
||||
realpath(__DIR__ . '/../vendor/amphp'),
|
||||
realpath(__DIR__ . '/../vendor/felixfbecker'),
|
||||
realpath(__DIR__ . '/../vendor/twig/twig'),
|
||||
realpath(__DIR__ . '/../vendor/guzzlehttp/guzzle'),
|
||||
realpath(__DIR__ . '/../vendor/slickdeals'),
|
||||
realpath(__DIR__ . '/../vendor/psr/log'),
|
||||
realpath(__DIR__ . '/../vendor/matomo'),
|
||||
realpath(__DIR__ . '/../vendor/symfony'),
|
||||
realpath(__DIR__ . '/../vendor/mongodb'),
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/websocket'), // TODO: remove workerman autoload
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/cache'), // TODO: remove memcached autoload
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/queue/src/Queue/Adapter/Workerman.php'), // TODO: remove workerman autoload
|
||||
] as $key => $value
|
||||
) {
|
||||
if ($value !== false) {
|
||||
$preloader->ignore($value);
|
||||
}
|
||||
}
|
||||
|
||||
$preloader
|
||||
->paths(realpath(__DIR__ . '/../app/config'))
|
||||
->paths(realpath(__DIR__ . '/../app/controllers'))
|
||||
->paths(realpath(__DIR__ . '/../src'))
|
||||
->load();
|
|
@ -50,7 +50,7 @@
|
|||
"utopia-php/cache": "0.9.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.48.2",
|
||||
"utopia-php/database": "0.48.*",
|
||||
"utopia-php/domains": "0.5.*",
|
||||
"utopia-php/dsn": "0.2.*",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
|
@ -62,7 +62,6 @@
|
|||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.5.*",
|
||||
"utopia-php/pools": "0.4.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.7.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.18.*",
|
||||
|
|
95
composer.lock
generated
95
composer.lock
generated
|
@ -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": "510f3498b35c5ec3289b7bed6a2ef9c9",
|
||||
"content-hash": "f19c09e7e233fe0f767bf5255fb46b86",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -1552,16 +1552,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.48.2",
|
||||
"version": "0.48.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4"
|
||||
"reference": "02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4",
|
||||
"reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0",
|
||||
"reference": "02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1569,7 +1569,7 @@
|
|||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cache": "0.9.*",
|
||||
"utopia-php/framework": "0.*.*",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/mongo": "0.3.*"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -1602,9 +1602,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.48.2"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.48.4"
|
||||
},
|
||||
"time": "2024-02-02T14:10:14+00:00"
|
||||
"time": "2024-02-23T03:22:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -2218,59 +2218,6 @@
|
|||
},
|
||||
"time": "2022-11-22T07:55:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/preloader",
|
||||
"version": "0.2.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/preloader.git",
|
||||
"reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0",
|
||||
"reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Utopia\\Preloader\\": "src/Preloader"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Eldad Fux",
|
||||
"email": "team@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"php",
|
||||
"preload",
|
||||
"preloader",
|
||||
"preloading",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/preloader/issues",
|
||||
"source": "https://github.com/utopia-php/preloader/tree/0.2.4"
|
||||
},
|
||||
"time": "2020-10-24T07:04:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/queue",
|
||||
"version": "0.7.0",
|
||||
|
@ -3131,16 +3078,16 @@
|
|||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.0.0",
|
||||
"version": "v5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
|
||||
"reference": "2218c2252c874a4624ab2f613d86ac32d227bc69"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69",
|
||||
"reference": "2218c2252c874a4624ab2f613d86ac32d227bc69",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3183,9 +3130,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1"
|
||||
},
|
||||
"time": "2024-01-07T17:17:35+00:00"
|
||||
"time": "2024-02-21T19:24:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
|
@ -3410,21 +3357,21 @@
|
|||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353"
|
||||
"reference": "153ae662783729388a584b4361f2545e4d841e3c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353",
|
||||
"reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c",
|
||||
"reference": "153ae662783729388a584b4361f2545e4d841e3c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/deprecations": "^1.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"phpdocumentor/reflection-common": "^2.0",
|
||||
"phpstan/phpdoc-parser": "^1.13"
|
||||
},
|
||||
|
@ -3462,9 +3409,9 @@
|
|||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.1"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2"
|
||||
},
|
||||
"time": "2024-01-18T19:15:27+00:00"
|
||||
"time": "2024-02-23T11:10:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
|
|
|
@ -92,7 +92,6 @@ services:
|
|||
- php
|
||||
- -e
|
||||
- app/http.php
|
||||
- -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
|
|
|
@ -30,31 +30,34 @@ class V20 extends Migration
|
|||
foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables', 'subQueryChallenges', 'subQueryProjectVariables', 'subQueryTargets', 'subQueryTopicTargets'] as $name) {
|
||||
Database::addFilter(
|
||||
$name,
|
||||
fn() => null,
|
||||
fn() => []
|
||||
fn () => null,
|
||||
fn () => []
|
||||
);
|
||||
}
|
||||
|
||||
$this->migrateUsageMetrics('project.$all.network.requests', 'network.requests');
|
||||
$this->migrateUsageMetrics('project.$all.network.outbound', 'network.outbound');
|
||||
$this->migrateUsageMetrics('project.$all.network.inbound', 'network.inbound');
|
||||
$this->migrateUsageMetrics('users.$all.count.total', 'users');
|
||||
$this->migrateSessionsMetric();
|
||||
|
||||
Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
|
||||
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
|
||||
|
||||
Console::info('Migrating Collections');
|
||||
$this->migrateCollections();
|
||||
|
||||
Console::info('Migrating Functions');
|
||||
$this->migrateFunctions();
|
||||
// No need to migrate stats for console
|
||||
if ($this->project->getInternalId() !== 'console') {
|
||||
$this->migrateUsageMetrics('project.$all.network.requests', 'network.requests');
|
||||
$this->migrateUsageMetrics('project.$all.network.outbound', 'network.outbound');
|
||||
$this->migrateUsageMetrics('project.$all.network.inbound', 'network.inbound');
|
||||
$this->migrateUsageMetrics('users.$all.count.total', 'users');
|
||||
$this->migrateSessionsMetric();
|
||||
|
||||
Console::info('Migrating Databases');
|
||||
$this->migrateDatabases();
|
||||
Console::info('Migrating Functions');
|
||||
$this->migrateFunctions();
|
||||
|
||||
Console::info('Migrating Buckets');
|
||||
$this->migrateBuckets();
|
||||
Console::info('Migrating Databases');
|
||||
$this->migrateDatabases();
|
||||
|
||||
Console::info('Migrating Buckets');
|
||||
$this->migrateBuckets();
|
||||
}
|
||||
|
||||
Console::info('Migrating Documents');
|
||||
$this->forEachDocument([$this, 'fixDocument']);
|
||||
|
@ -75,25 +78,27 @@ class V20 extends Migration
|
|||
};
|
||||
|
||||
// Support database array type migration (user collections)
|
||||
foreach (
|
||||
$this->documentsIterator('attributes', [
|
||||
Query::equal('array', [true]),
|
||||
]) as $attribute
|
||||
) {
|
||||
$foundIndex = false;
|
||||
if ($collectionType === 'projects') {
|
||||
foreach (
|
||||
$this->documentsIterator('indexes', [
|
||||
Query::equal('databaseInternalId', [$attribute['databaseInternalId']]),
|
||||
Query::equal('collectionInternalId', [$attribute['collectionInternalId']]),
|
||||
]) as $index
|
||||
$this->documentsIterator('attributes', [
|
||||
Query::equal('array', [true]),
|
||||
]) as $attribute
|
||||
) {
|
||||
if (in_array($attribute['key'], $index['attributes'])) {
|
||||
$this->projectDB->deleteIndex($index['collectionId'], $index['$id']);
|
||||
$foundIndex = true;
|
||||
$foundIndex = false;
|
||||
foreach (
|
||||
$this->documentsIterator('indexes', [
|
||||
Query::equal('databaseInternalId', [$attribute['databaseInternalId']]),
|
||||
Query::equal('collectionInternalId', [$attribute['collectionInternalId']]),
|
||||
]) as $index
|
||||
) {
|
||||
if (in_array($attribute['key'], $index['attributes'])) {
|
||||
$this->projectDB->deleteIndex($index['collectionId'], $index['$id']);
|
||||
$foundIndex = true;
|
||||
}
|
||||
}
|
||||
if ($foundIndex === true) {
|
||||
$this->projectDB->updateAttribute($attribute['collectionInternalId'], $attribute['key'], $attribute['type']);
|
||||
}
|
||||
}
|
||||
if ($foundIndex === true) {
|
||||
$this->projectDB->updateAttribute($attribute['collectionInternalId'], $attribute['key'], $attribute['type']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +328,6 @@ class V20 extends Migration
|
|||
*/
|
||||
protected function createInfMetric(string $metric, int $value): void
|
||||
{
|
||||
|
||||
try {
|
||||
/**
|
||||
* Creating inf metric
|
||||
|
@ -351,7 +355,6 @@ class V20 extends Migration
|
|||
*/
|
||||
protected function migrateUsageMetrics(string $from, string $to): void
|
||||
{
|
||||
|
||||
/**
|
||||
* inf metric
|
||||
*/
|
||||
|
@ -411,7 +414,6 @@ class V20 extends Migration
|
|||
*/
|
||||
private function migrateFunctions(): void
|
||||
{
|
||||
|
||||
$this->migrateUsageMetrics('deployment.$all.storage.size', 'deployments.storage');
|
||||
$this->migrateUsageMetrics('builds.$all.compute.total', 'builds');
|
||||
$this->migrateUsageMetrics('builds.$all.compute.time', 'builds.compute');
|
||||
|
@ -539,6 +541,14 @@ class V20 extends Migration
|
|||
$duration = $this->project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::addSeconds(new \DateTime(), $duration);
|
||||
$document->setAttribute('expire', $expire);
|
||||
|
||||
$factors = match ($document->getAttribute('provider')) {
|
||||
Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
|
||||
Auth::SESSION_PROVIDER_PHONE => ['phone'],
|
||||
default => ['password'],
|
||||
};
|
||||
|
||||
$document->setAttribute('factors', $factors);
|
||||
break;
|
||||
}
|
||||
return $document;
|
||||
|
|
|
@ -32,6 +32,14 @@ class Mails extends Action
|
|||
->callback(fn (Message $message, Registry $register, Log $log) => $this->action($message, $register, $log));
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected array $richTextParams = [
|
||||
'b' => '<strong>',
|
||||
'/b' => '</strong>',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Message $message
|
||||
* @param Registry $register
|
||||
|
@ -81,6 +89,9 @@ class Mails extends Action
|
|||
// TODO: hotfix for redirect param
|
||||
$bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: $key !== 'redirect');
|
||||
}
|
||||
foreach ($this->richTextParams as $key => $value) {
|
||||
$bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: false);
|
||||
}
|
||||
$body = $bodyTemplate->render();
|
||||
|
||||
$subjectTemplate = Template::fromString($subject);
|
||||
|
|
|
@ -469,6 +469,7 @@ class Messaging extends Action
|
|||
private function getPushAdapter(Document $provider): ?PushAdapter
|
||||
{
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
$options = $provider->getAttribute('options');
|
||||
|
||||
return match ($provider->getAttribute('provider')) {
|
||||
'mock' => new Mock('username', 'password'),
|
||||
|
@ -477,6 +478,7 @@ class Messaging extends Action
|
|||
$credentials['authKeyId'],
|
||||
$credentials['teamId'],
|
||||
$credentials['bundleId'],
|
||||
$options['sandbox']
|
||||
),
|
||||
'fcm' => new FCM(\json_encode($credentials['serviceAccountJSON'])),
|
||||
default => null
|
||||
|
|
|
@ -116,6 +116,7 @@ abstract class Format
|
|||
case 'account':
|
||||
switch ($method) {
|
||||
case 'createOAuth2Session':
|
||||
case 'createOAuth2Token':
|
||||
switch ($param) {
|
||||
case 'provider':
|
||||
return 'OAuthProvider';
|
||||
|
@ -219,11 +220,11 @@ abstract class Format
|
|||
return 'MessageStatus';
|
||||
}
|
||||
break;
|
||||
case 'createSMTPProvider':
|
||||
case 'updateSMTPProvider':
|
||||
case 'createSmtpProvider':
|
||||
case 'updateSmtpProvider':
|
||||
switch ($param) {
|
||||
case 'encryption':
|
||||
return 'SMTPEncryption';
|
||||
return 'SmtpEncryption';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,10 @@ class V17 extends Filter
|
|||
|
||||
private function convertOldQueries(array $content): array
|
||||
{
|
||||
if (!isset($content['queries'])) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$parsed = [];
|
||||
foreach ($content['queries'] as $query) {
|
||||
try {
|
||||
|
|
|
@ -2226,7 +2226,7 @@ class AccountCustomClientTest extends Scope
|
|||
/**
|
||||
* @depends testUpdatePhone
|
||||
*/
|
||||
#[Retry(count: 2)]
|
||||
#[Retry(count: 3)]
|
||||
public function testPhoneVerification(array $data): array
|
||||
{
|
||||
$session = $data['session'] ?? '';
|
||||
|
|
|
@ -2973,7 +2973,7 @@ trait DatabasesBase
|
|||
$this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']);
|
||||
$this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']);
|
||||
$this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']);
|
||||
$this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,808', $tooLow['body']['message']);
|
||||
$this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,807', $tooLow['body']['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -135,7 +135,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
\sleep(1);
|
||||
}
|
||||
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
$this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body']));
|
||||
|
||||
$function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
|
||||
'content-type' => 'application/json',
|
||||
|
@ -266,7 +266,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
\sleep(1);
|
||||
}
|
||||
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
$this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body']));
|
||||
|
||||
$function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [
|
||||
'content-type' => 'application/json',
|
||||
|
|
|
@ -1812,8 +1812,8 @@ trait Base
|
|||
}
|
||||
}';
|
||||
case self::$CREATE_SMTP_PROVIDER:
|
||||
return 'mutation createSMTPProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean! $fromName: String!, $fromEmail: String!, $replyToName: String, $replyToEmail: String) {
|
||||
messagingCreateSMTPProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, replyToName: $replyToName, replyToEmail: $replyToEmail) {
|
||||
return 'mutation createSmtpProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean! $fromName: String!, $fromEmail: String!, $replyToName: String, $replyToEmail: String) {
|
||||
messagingCreateSmtpProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, replyToName: $replyToName, replyToEmail: $replyToEmail) {
|
||||
_id
|
||||
name
|
||||
provider
|
||||
|
@ -1936,8 +1936,8 @@ trait Base
|
|||
}
|
||||
}';
|
||||
case self::$UPDATE_SMTP_PROVIDER:
|
||||
return 'mutation updateSMTPProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean!, $fromName: String, $fromEmail: String, $enabled: Boolean) {
|
||||
messagingUpdateSMTPProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, enabled: $enabled) {
|
||||
return 'mutation updateSmtpProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean!, $fromName: String, $fromEmail: String, $enabled: Boolean) {
|
||||
messagingUpdateSmtpProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, enabled: $enabled) {
|
||||
_id
|
||||
name
|
||||
provider
|
||||
|
|
|
@ -98,16 +98,22 @@ trait MessagingBase
|
|||
];
|
||||
$providers = [];
|
||||
|
||||
foreach (\array_keys($providersParams) as $key) {
|
||||
foreach ($providersParams as $key => $params) {
|
||||
$response = $this->client->call(Client::METHOD_POST, '/messaging/providers/' . $key, \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $providersParams[$key]);
|
||||
]), $params);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertEquals($providersParams[$key]['name'], $response['body']['name']);
|
||||
\array_push($providers, $response['body']);
|
||||
$this->assertEquals($params['name'], $response['body']['name']);
|
||||
$providers[] = $response['body'];
|
||||
|
||||
switch ($key) {
|
||||
case 'apns':
|
||||
$this->assertEquals(false, $response['body']['options']['sandbox']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
|
@ -341,6 +347,17 @@ trait MessagingBase
|
|||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('android-app', $response['body']['name']);
|
||||
|
||||
$response2 = $this->client->call(Client::METHOD_PATCH, '/messaging/topics/' . $topics['private']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'name' => 'ios-app',
|
||||
'subscribe' => [Role::user('some-user')->toString()],
|
||||
]);
|
||||
$this->assertEquals(200, $response2['headers']['status-code']);
|
||||
$this->assertEquals('ios-app', $response2['body']['name']);
|
||||
|
||||
return $response['body']['$id'];
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,6 @@ class ExtensionsTest extends TestCase
|
|||
$this->assertEquals(true, extension_loaded('yaml'));
|
||||
}
|
||||
|
||||
public function testOPCache(): void
|
||||
{
|
||||
$this->assertEquals(true, extension_loaded('Zend OPcache'));
|
||||
}
|
||||
|
||||
public function testDOM(): void
|
||||
{
|
||||
$this->assertEquals(true, extension_loaded('dom'));
|
||||
|
|
Loading…
Reference in a new issue