mirror of
https://github.com/appwrite/appwrite
synced 2026-05-24 01:18:37 +00:00
Add failure email
This commit is contained in:
parent
fdf5367e39
commit
e222221ec9
2 changed files with 97 additions and 35 deletions
|
|
@ -57,14 +57,21 @@
|
||||||
"emails.recovery.thanks": "Thanks,",
|
"emails.recovery.thanks": "Thanks,",
|
||||||
"emails.recovery.buttonText": "Reset password",
|
"emails.recovery.buttonText": "Reset password",
|
||||||
"emails.recovery.signature": "{{project}} team",
|
"emails.recovery.signature": "{{project}} team",
|
||||||
"emails.csvExport.subject": "Your CSV export is ready",
|
"emails.csvExport.success.subject": "Your CSV export is ready",
|
||||||
"emails.csvExport.preview": "Your data export has been completed successfully.",
|
"emails.csvExport.success.preview": "Your data export has been completed successfully.",
|
||||||
"emails.csvExport.hello": "Hello {{user}},",
|
"emails.csvExport.success.hello": "Hello {{user}},",
|
||||||
"emails.csvExport.body": "Your CSV export is ready for download. Click the link below to download your data export.",
|
"emails.csvExport.success.body": "Your CSV export is ready for download. Click the link below to download your data export.",
|
||||||
"emails.csvExport.footer": "This download link will expire in 1 hour.",
|
"emails.csvExport.success.footer": "This download link will expire in 1 hour.",
|
||||||
"emails.csvExport.thanks": "Thanks,",
|
"emails.csvExport.success.thanks": "Thanks,",
|
||||||
"emails.csvExport.buttonText": "Download CSV",
|
"emails.csvExport.success.buttonText": "Download CSV",
|
||||||
"emails.csvExport.signature": "{{project}} team",
|
"emails.csvExport.success.signature": "{{project}} team",
|
||||||
|
"emails.csvExport.failure.subject": "Your CSV export failed - file too large",
|
||||||
|
"emails.csvExport.failure.preview": "Your data export failed because the file size exceeds your plan limit.",
|
||||||
|
"emails.csvExport.failure.hello": "Hello {{user}},",
|
||||||
|
"emails.csvExport.failure.body": "Your CSV export could not be completed because the export file size ({{size}}MB) exceeds your plan limit. Please consider upgrading your plan or exporting a smaller dataset.",
|
||||||
|
"emails.csvExport.failure.footer": "If you have any questions, please contact our support team.",
|
||||||
|
"emails.csvExport.failure.thanks": "Thanks,",
|
||||||
|
"emails.csvExport.failure.signature": "{{project}} team",
|
||||||
"emails.invitation.subject": "Invitation to {{team}} Team at {{project}}",
|
"emails.invitation.subject": "Invitation to {{team}} Team at {{project}}",
|
||||||
"emails.invitation.preview": "{{owner}} invited you to join {{team}} at {{project}}",
|
"emails.invitation.preview": "{{owner}} invited you to join {{team}} at {{project}}",
|
||||||
"emails.invitation.hello": "Hello {{user}},",
|
"emails.invitation.hello": "Hello {{user}},",
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ namespace Appwrite\Platform\Workers;
|
||||||
use Ahc\Jwt\JWT;
|
use Ahc\Jwt\JWT;
|
||||||
use Appwrite\Event\Mail;
|
use Appwrite\Event\Mail;
|
||||||
use Appwrite\Event\Realtime;
|
use Appwrite\Event\Realtime;
|
||||||
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Template\Template;
|
use Appwrite\Template\Template;
|
||||||
use Exception;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
|
|
@ -458,16 +458,27 @@ class Migrations extends Action
|
||||||
$fileId = ID::unique();
|
$fileId = ID::unique();
|
||||||
|
|
||||||
$sizeMB = \round($size / (1000 * 1000), 2);
|
$sizeMB = \round($size / (1000 * 1000), 2);
|
||||||
if ($sizeMB > $plan['fileSize'] ?? PHP_INT_MAX) {
|
if ($sizeMB > $this->plan['fileSize'] ?? PHP_INT_MAX) {
|
||||||
try {
|
try {
|
||||||
$this->deviceForFiles->delete($path);
|
$this->deviceForFiles->delete($path);
|
||||||
} finally {
|
} finally {
|
||||||
$message = "Export file size {$sizeMB}MB exceeds your plan limit.";
|
$message = "Export file size {$sizeMB}MB exceeds your plan limit.";
|
||||||
|
|
||||||
$this->dbForProject->updateDocument('migrations', $migration->getId(), $migration->setAttribute(
|
$this->dbForProject->updateDocument('migrations', $migration->getId(), $migration->setAttribute(
|
||||||
'errors',
|
'errors',
|
||||||
$message,
|
$message,
|
||||||
Document::SET_TYPE_APPEND,
|
Document::SET_TYPE_APPEND,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$this->sendCSVEmail(
|
||||||
|
success: false,
|
||||||
|
project: $project,
|
||||||
|
userInternalId: $userInternalId,
|
||||||
|
options: $options,
|
||||||
|
queueForMails: $queueForMails,
|
||||||
|
sizeMB: $sizeMB
|
||||||
|
);
|
||||||
|
|
||||||
throw new \Exception($message);
|
throw new \Exception($message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -497,7 +508,52 @@ class Migrations extends Action
|
||||||
|
|
||||||
Console::info("Created file document in bucket: $fileId");
|
Console::info("Created file document in bucket: $fileId");
|
||||||
|
|
||||||
// No notification required, skip email sending
|
// Generate JWT valid for 1 hour
|
||||||
|
$maxAge = 60 * 60;
|
||||||
|
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $maxAge, 0);
|
||||||
|
$jwt = $encoder->encode([
|
||||||
|
'bucketId' => $bucketId,
|
||||||
|
'fileId' => $fileId,
|
||||||
|
'projectId' => $project->getId(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Generate download URL with JWT
|
||||||
|
$endpoint = System::getEnv('_APP_DOMAIN', '');
|
||||||
|
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled' ? 'https' : 'http';
|
||||||
|
$downloadUrl = "{$protocol}://{$endpoint}/v1/storage/buckets/{$bucketId}/files/{$fileId}/push?project={$project->getId()}&jwt={$jwt}";
|
||||||
|
|
||||||
|
$this->sendCSVEmail(
|
||||||
|
success: true,
|
||||||
|
project: $project,
|
||||||
|
userInternalId: $userInternalId,
|
||||||
|
options: $options,
|
||||||
|
queueForMails: $queueForMails,
|
||||||
|
downloadUrl: $downloadUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send CSV export notification email
|
||||||
|
*
|
||||||
|
* @param bool $success Whether the export was successful
|
||||||
|
* @param Document $project
|
||||||
|
* @param string $userInternalId Internal ID of the user
|
||||||
|
* @param array $options Migration options
|
||||||
|
* @param Mail $queueForMails
|
||||||
|
* @param string $downloadUrl Download URL for successful exports
|
||||||
|
* @param float $sizeMB File size in MB for failed exports
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected function sendCSVEmail(
|
||||||
|
bool $success,
|
||||||
|
Document $project,
|
||||||
|
string $userInternalId,
|
||||||
|
array $options,
|
||||||
|
Mail $queueForMails,
|
||||||
|
string $downloadUrl = '',
|
||||||
|
float $sizeMB = 0.0
|
||||||
|
): void {
|
||||||
if (!($options['notify'] ?? false)) {
|
if (!($options['notify'] ?? false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -514,29 +570,19 @@ class Migrations extends Action
|
||||||
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
|
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
|
||||||
$locale->setFallback(System::getEnv('_APP_LOCALE', 'en'));
|
$locale->setFallback(System::getEnv('_APP_LOCALE', 'en'));
|
||||||
|
|
||||||
// Generate JWT valid for 1 hour
|
$emailType = $success
|
||||||
$maxAge = 60 * 60;
|
? 'success'
|
||||||
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $maxAge, 0);
|
: 'failure';
|
||||||
$jwt = $encoder->encode([
|
|
||||||
'bucketId' => $bucketId,
|
|
||||||
'fileId' => $fileId,
|
|
||||||
'projectId' => $project->getId(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Generate download URL with JWT
|
|
||||||
$endpoint = System::getEnv('_APP_DOMAIN', '');
|
|
||||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled' ? 'https' : 'http';
|
|
||||||
$downloadUrl = "{$protocol}://{$endpoint}/v1/storage/buckets/{$bucketId}/files/{$fileId}/push?project={$project->getId()}&jwt={$jwt}";
|
|
||||||
|
|
||||||
// Get localized email content
|
// Get localized email content
|
||||||
$subject = $locale->getText('emails.csvExport.subject');
|
$subject = $locale->getText("emails.csvExport.{$emailType}.subject");
|
||||||
$preview = $locale->getText('emails.csvExport.preview');
|
$preview = $locale->getText("emails.csvExport.{$emailType}.preview");
|
||||||
$hello = $locale->getText('emails.csvExport.hello');
|
$hello = $locale->getText("emails.csvExport.{$emailType}.hello");
|
||||||
$body = $locale->getText('emails.csvExport.body');
|
$body = $locale->getText("emails.csvExport.{$emailType}.body");
|
||||||
$footer = $locale->getText('emails.csvExport.footer');
|
$footer = $locale->getText("emails.csvExport.{$emailType}.footer");
|
||||||
$thanks = $locale->getText('emails.csvExport.thanks');
|
$thanks = $locale->getText("emails.csvExport.{$emailType}.thanks");
|
||||||
$buttonText = $locale->getText('emails.csvExport.buttonText');
|
$signature = $locale->getText("emails.csvExport.{$emailType}.signature");
|
||||||
$signature = $locale->getText('emails.csvExport.signature');
|
$buttonText = $success ? $locale->getText("emails.csvExport.{$emailType}.buttonText") : '';
|
||||||
|
|
||||||
// Build email body using inner template
|
// Build email body using inner template
|
||||||
$message = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-inner-base.tpl')
|
$message = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-inner-base.tpl')
|
||||||
|
|
@ -549,7 +595,8 @@ class Migrations extends Action
|
||||||
->setParam('{{direction}}', $locale->getText('settings.direction'))
|
->setParam('{{direction}}', $locale->getText('settings.direction'))
|
||||||
->setParam('{{project}}', $project->getAttribute('name'))
|
->setParam('{{project}}', $project->getAttribute('name'))
|
||||||
->setParam('{{user}}', $user->getAttribute('name', $user->getAttribute('email')))
|
->setParam('{{user}}', $user->getAttribute('name', $user->getAttribute('email')))
|
||||||
->setParam('{{redirect}}', $downloadUrl);
|
->setParam('{{redirect}}', $downloadUrl)
|
||||||
|
->setParam('{{size}}', $success ? '' : (string)$sizeMB);
|
||||||
|
|
||||||
$emailBody = $message->render();
|
$emailBody = $message->render();
|
||||||
|
|
||||||
|
|
@ -557,9 +604,14 @@ class Migrations extends Action
|
||||||
'direction' => $locale->getText('settings.direction'),
|
'direction' => $locale->getText('settings.direction'),
|
||||||
'project' => $project->getAttribute('name'),
|
'project' => $project->getAttribute('name'),
|
||||||
'user' => $user->getAttribute('name', $user->getAttribute('email')),
|
'user' => $user->getAttribute('name', $user->getAttribute('email')),
|
||||||
'redirect' => $downloadUrl,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
$emailVariables['redirect'] = $downloadUrl;
|
||||||
|
} else {
|
||||||
|
$emailVariables['size'] = (string)$sizeMB;
|
||||||
|
}
|
||||||
|
|
||||||
$queueForMails
|
$queueForMails
|
||||||
->setSubject($subject)
|
->setSubject($subject)
|
||||||
->setPreview($preview)
|
->setPreview($preview)
|
||||||
|
|
@ -570,11 +622,14 @@ class Migrations extends Action
|
||||||
->setRecipient($user->getAttribute('email'))
|
->setRecipient($user->getAttribute('email'))
|
||||||
->trigger();
|
->trigger();
|
||||||
|
|
||||||
Console::info('CSV export notification email sent to ' . $user->getAttribute('email'));
|
Console::info("CSV export {$emailType} notification email sent to " . $user->getAttribute('email'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitize a filename to make it filesystem-safe
|
* Sanitize a filename to make it filesystem-safe
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function sanitizeFilename(string $filename): string
|
protected function sanitizeFilename(string $filename): string
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue