able to use resend for pro+ users
This commit is contained in:
parent
2538890b52
commit
ae8bd69106
@ -12,20 +12,21 @@ class ServerController extends Controller
|
||||
|
||||
public function new_server()
|
||||
{
|
||||
$privateKeys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
if (!isCloud()) {
|
||||
return view('server.create', [
|
||||
'limit_reached' => false,
|
||||
'private_keys' => PrivateKey::ownedByCurrentTeam()->get(),
|
||||
'private_keys' => $privateKeys,
|
||||
]);
|
||||
}
|
||||
$servers = currentTeam()->servers->count();
|
||||
$subscription = currentTeam()?->subscription->type();
|
||||
$your_limit = config('constants.limits.server')[strtolower($subscription)];
|
||||
$limit_reached = $servers >= $your_limit;
|
||||
$team = currentTeam();
|
||||
$servers = $team->servers->count();
|
||||
['serverLimit' => $serverLimit] = $team->limits;
|
||||
$limit_reached = $servers >= $serverLimit;
|
||||
|
||||
return view('server.create', [
|
||||
'limit_reached' => $limit_reached,
|
||||
'private_keys' => PrivateKey::ownedByCurrentTeam()->get(),
|
||||
'private_keys' => $privateKeys,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
use App\Models\S3Storage;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
use Log;
|
||||
|
||||
class Dashboard extends Component
|
||||
{
|
||||
|
@ -6,55 +6,143 @@
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Component;
|
||||
use Log;
|
||||
|
||||
class EmailSettings extends Component
|
||||
{
|
||||
public Team $model;
|
||||
public Team $team;
|
||||
public string $emails;
|
||||
public bool $sharedEmailEnabled = false;
|
||||
|
||||
protected $rules = [
|
||||
'model.smtp_enabled' => 'nullable|boolean',
|
||||
'model.smtp_from_address' => 'required|email',
|
||||
'model.smtp_from_name' => 'required',
|
||||
'model.smtp_recipients' => 'nullable',
|
||||
'model.smtp_host' => 'required',
|
||||
'model.smtp_port' => 'required',
|
||||
'model.smtp_encryption' => 'nullable',
|
||||
'model.smtp_username' => 'nullable',
|
||||
'model.smtp_password' => 'nullable',
|
||||
'model.smtp_timeout' => 'nullable',
|
||||
'model.smtp_notifications_test' => 'nullable|boolean',
|
||||
'model.smtp_notifications_deployments' => 'nullable|boolean',
|
||||
'model.smtp_notifications_status_changes' => 'nullable|boolean',
|
||||
'model.smtp_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.smtp_enabled' => 'nullable|boolean',
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.smtp_recipients' => 'nullable',
|
||||
'team.smtp_host' => 'required',
|
||||
'team.smtp_port' => 'required',
|
||||
'team.smtp_encryption' => 'nullable',
|
||||
'team.smtp_username' => 'nullable',
|
||||
'team.smtp_password' => 'nullable',
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
'team.smtp_notifications_test' => 'nullable|boolean',
|
||||
'team.smtp_notifications_deployments' => 'nullable|boolean',
|
||||
'team.smtp_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.smtp_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.use_instance_email_settings' => 'boolean',
|
||||
'team.resend_enabled' => 'nullable|boolean',
|
||||
'team.resend_api_key' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'model.smtp_from_address' => 'From Address',
|
||||
'model.smtp_from_name' => 'From Name',
|
||||
'model.smtp_recipients' => 'Recipients',
|
||||
'model.smtp_host' => 'Host',
|
||||
'model.smtp_port' => 'Port',
|
||||
'model.smtp_encryption' => 'Encryption',
|
||||
'model.smtp_username' => 'Username',
|
||||
'model.smtp_password' => 'Password',
|
||||
'team.smtp_from_address' => 'From Address',
|
||||
'team.smtp_from_name' => 'From Name',
|
||||
'team.smtp_recipients' => 'Recipients',
|
||||
'team.smtp_host' => 'Host',
|
||||
'team.smtp_port' => 'Port',
|
||||
'team.smtp_encryption' => 'Encryption',
|
||||
'team.smtp_username' => 'Username',
|
||||
'team.smtp_password' => 'Password',
|
||||
'team.smtp_timeout' => 'Timeout',
|
||||
'team.resend_enabled' => 'Resend Enabled',
|
||||
'team.resend_api_key' => 'Resend API Key',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->decrypt();
|
||||
['sharedEmailEnabled' => $this->sharedEmailEnabled] = $this->team->limits;
|
||||
$this->emails = auth()->user()->email;
|
||||
}
|
||||
|
||||
private function decrypt()
|
||||
public function submitFromFields()
|
||||
{
|
||||
if (data_get($this->model, 'smtp_password')) {
|
||||
try {
|
||||
$this->model->smtp_password = decrypt($this->model->smtp_password);
|
||||
} catch (\Exception $e) {
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
]);
|
||||
$this->team->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team->notify(new Test($this->emails));
|
||||
$this->emit('success', 'Test Email sent successfully.');
|
||||
}
|
||||
public function instantSaveInstance()
|
||||
{
|
||||
try {
|
||||
if (!$this->sharedEmailEnabled) {
|
||||
throw new \Exception('Not allowed to change settings. Please upgrade your subscription.');
|
||||
}
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->team->resend_enabled = false;
|
||||
$this->team->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveResend()
|
||||
{
|
||||
try {
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->submitResend();
|
||||
} catch (\Exception $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->team->resend_enabled = false;
|
||||
$this->submit();
|
||||
} catch (\Exception $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.smtp_host' => 'required',
|
||||
'team.smtp_port' => 'required|numeric',
|
||||
'team.smtp_encryption' => 'nullable',
|
||||
'team.smtp_username' => 'nullable',
|
||||
'team.smtp_password' => 'nullable',
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
$this->team->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function submitResend()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'team.resend_api_key' => 'required'
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
$this->team->resend_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function copyFromInstanceSettings()
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
@ -72,55 +160,22 @@ public function copyFromInstanceSettings()
|
||||
'smtp_password' => $settings->smtp_password,
|
||||
'smtp_timeout' => $settings->smtp_timeout,
|
||||
]);
|
||||
$this->decrypt();
|
||||
if (is_a($team, Team::class)) {
|
||||
refreshSession();
|
||||
}
|
||||
$this->model = $team;
|
||||
$this->emit('success', 'Settings saved.');
|
||||
} else {
|
||||
$this->emit('error', 'Instance SMTP settings are not enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->model->notify(new Test($this->emails));
|
||||
$this->emit('success', 'Test Email sent successfully.');
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
} catch (\Exception $e) {
|
||||
$this->model->smtp_enabled = false;
|
||||
$this->validate();
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->resetErrorBag();
|
||||
$this->validate();
|
||||
|
||||
if ($this->model->smtp_password) {
|
||||
$this->model->smtp_password = encrypt($this->model->smtp_password);
|
||||
} else {
|
||||
$this->model->smtp_password = null;
|
||||
}
|
||||
|
||||
$this->model->smtp_recipients = str_replace(' ', '', $this->model->smtp_recipients);
|
||||
$this->saveModel();
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->model->save();
|
||||
$this->decrypt();
|
||||
if (is_a($this->model, Team::class)) {
|
||||
refreshSession();
|
||||
$this->team = $team;
|
||||
$this->emit('success', 'Settings saved.');
|
||||
return;
|
||||
}
|
||||
$this->emit('success', 'Settings saved.');
|
||||
if ($settings->resend_enabled) {
|
||||
$team = currentTeam();
|
||||
$team->update([
|
||||
'resend_enabled' => $settings->resend_enabled,
|
||||
'resend_api_key' => $settings->resend_api_key,
|
||||
]);
|
||||
refreshSession();
|
||||
$this->team = $team;
|
||||
$this->emit('success', 'Settings saved.');
|
||||
return;
|
||||
}
|
||||
$this->emit('error', 'Instance SMTP/Resend settings are not enabled.');
|
||||
}
|
||||
}
|
||||
|
@ -54,14 +54,6 @@ public function submitFromFields() {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSaveResend()
|
||||
{
|
||||
try {
|
||||
$this->submitResend();
|
||||
} catch (\Exception $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function submitResend() {
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
|
@ -14,6 +14,7 @@ public function deleteInvitation(int $invitation_id)
|
||||
{
|
||||
TeamInvitation::find($invitation_id)->delete();
|
||||
$this->refreshInvitations();
|
||||
$this->emit('success', 'Invitation revoked.');
|
||||
}
|
||||
|
||||
public function refreshInvitations()
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Notifications\Channels\SendsDiscord;
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
@ -14,6 +15,8 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
'personal_team' => 'boolean',
|
||||
'smtp_password' => 'encrypted',
|
||||
'resend_api_key' => 'encrypted',
|
||||
];
|
||||
|
||||
public function routeNotificationForDiscord()
|
||||
@ -30,6 +33,27 @@ public function getRecepients($notification)
|
||||
}
|
||||
return explode(',', $recipients);
|
||||
}
|
||||
public function limits(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
if (config('coolify.self_hosted') || $this->id === 0) {
|
||||
$subscription = 'self-hosted';
|
||||
} else {
|
||||
$subscription = data_get($this, 'subscription');
|
||||
if (is_null($subscription)) {
|
||||
$subscription = 'zero';
|
||||
} else {
|
||||
$subscription = $subscription->type();
|
||||
}
|
||||
}
|
||||
$serverLimit = config('constants.limits.server')[strtolower($subscription)];
|
||||
$sharedEmailEnabled = config('constants.limits.email')[strtolower($subscription)];
|
||||
return ['serverLimit' => $serverLimit, 'sharedEmailEnabled' => $sharedEmailEnabled];
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
public function members()
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ public function __construct(Application $application, string $deployment_uuid, A
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_deployments');
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_deployments');
|
||||
|
@ -21,7 +21,7 @@ public function send(SendsEmail $notifiable, Notification $notification): void
|
||||
|
||||
$mailMessage = $notification->toMail($notifiable);
|
||||
if ($this->isResend) {
|
||||
foreach($recepients as $receipient) {
|
||||
foreach ($recepients as $receipient) {
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
@ -35,7 +35,6 @@ public function send(SendsEmail $notifiable, Notification $notification): void
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
Mail::send(
|
||||
[],
|
||||
@ -50,22 +49,26 @@ public function send(SendsEmail $notifiable, Notification $notification): void
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function bootConfigs($notifiable): void
|
||||
{
|
||||
if (data_get($notifiable, 'resend_enabled')) {
|
||||
$resendAPIKey = data_get($notifiable, 'resend_api_key');
|
||||
if ($resendAPIKey) {
|
||||
$this->isResend = true;
|
||||
config()->set('mail.default', 'resend');
|
||||
config()->set('resend.api_key', $resendAPIKey);
|
||||
if (data_get($notifiable, 'use_instance_email_settings')) {
|
||||
$type = set_transanctional_email_settings();
|
||||
if (!$type) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
if ($type === 'resend') {
|
||||
$this->isResend = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (data_get($notifiable, 'resend_enabled')) {
|
||||
$this->isResend = true;
|
||||
config()->set('mail.default', 'resend');
|
||||
config()->set('resend.api_key', data_get($notifiable, 'resend_api_key'));
|
||||
}
|
||||
if (data_get($notifiable, 'smtp_enabled')) {
|
||||
$password = data_get($notifiable, 'smtp_password');
|
||||
if ($password) $password = decrypt($password);
|
||||
config()->set('mail.default', 'smtp');
|
||||
config()->set('mail.mailers.smtp', [
|
||||
"transport" => "smtp",
|
||||
@ -73,7 +76,7 @@ private function bootConfigs($notifiable): void
|
||||
"port" => data_get($notifiable, 'smtp_port'),
|
||||
"encryption" => data_get($notifiable, 'smtp_encryption'),
|
||||
"username" => data_get($notifiable, 'smtp_username'),
|
||||
"password" => $password,
|
||||
"password" => data_get($notifiable, 'smtp_password'),
|
||||
"timeout" => data_get($notifiable, 'smtp_timeout'),
|
||||
"local_domain" => null,
|
||||
]);
|
||||
|
@ -4,16 +4,20 @@
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Log;
|
||||
|
||||
class TransactionalEmailChannel
|
||||
{
|
||||
private bool $isResend = false;
|
||||
public function send(User $notifiable, Notification $notification): void
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
if (data_get($settings, 'smtp_enabled') !== true) {
|
||||
if (!data_get($settings, 'smtp_enabled') && !data_get($settings, 'resend_enabled')) {
|
||||
Log::info('SMTP/Resend not enabled');
|
||||
return;
|
||||
}
|
||||
$email = $notifiable->email;
|
||||
@ -22,22 +26,43 @@ public function send(User $notifiable, Notification $notification): void
|
||||
}
|
||||
$this->bootConfigs();
|
||||
$mailMessage = $notification->toMail($notifiable);
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
->from(
|
||||
data_get($settings, 'smtp_from_address'),
|
||||
data_get($settings, 'smtp_from_name')
|
||||
)
|
||||
->to($email)
|
||||
->subject($mailMessage->subject)
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
if ($this->isResend) {
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
->from(
|
||||
data_get($settings, 'smtp_from_address'),
|
||||
data_get($settings, 'smtp_from_name'),
|
||||
)
|
||||
->to($email)
|
||||
->subject($mailMessage->subject)
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
} else {
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
->from(
|
||||
data_get($settings, 'smtp_from_address'),
|
||||
data_get($settings, 'smtp_from_name'),
|
||||
)
|
||||
->bcc($email)
|
||||
->subject($mailMessage->subject)
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function bootConfigs(): void
|
||||
{
|
||||
set_transanctional_email_settings();
|
||||
$type = set_transanctional_email_settings();
|
||||
if (!$type) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
if ($type === 'resend') {
|
||||
$this->isResend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public function __construct(ScheduledDatabaseBackup $backup, public $database, p
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_database_backups');
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_database_backups');
|
||||
|
@ -25,7 +25,7 @@ public function __construct(ScheduledDatabaseBackup $backup, public $database)
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_database_backups');
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_database_backups');
|
||||
|
@ -23,7 +23,7 @@ public function __construct(public Server $server)
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_status_changes');
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_status_changes');
|
||||
|
@ -20,7 +20,7 @@ public function __construct(public string|null $emails = null)
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
|
||||
if ($isDiscordEnabled && empty($this->emails)) {
|
||||
|
@ -31,24 +31,11 @@ public static function toMailUsing($callback)
|
||||
|
||||
public function via($notifiable)
|
||||
{
|
||||
if ($this->settings->smtp_enabled) {
|
||||
$password = data_get($this->settings, 'smtp_password');
|
||||
if ($password) $password = decrypt($password);
|
||||
|
||||
config()->set('mail.default', 'smtp');
|
||||
config()->set('mail.mailers.smtp', [
|
||||
"transport" => "smtp",
|
||||
"host" => data_get($this->settings, 'smtp_host'),
|
||||
"port" => data_get($this->settings, 'smtp_port'),
|
||||
"encryption" => data_get($this->settings, 'smtp_encryption'),
|
||||
"username" => data_get($this->settings, 'smtp_username'),
|
||||
"password" => $password,
|
||||
"timeout" => data_get($this->settings, 'smtp_timeout'),
|
||||
"local_domain" => null,
|
||||
]);
|
||||
return ['mail'];
|
||||
$type = set_transanctional_email_settings();
|
||||
if (!$type) {
|
||||
throw new \Exception('No email settings found.');
|
||||
}
|
||||
throw new \Exception('SMTP is not enabled');
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
public function toMail($notifiable)
|
||||
|
@ -73,7 +73,7 @@ function general_error_handler(Throwable | null $err = null, $that = null, $isJs
|
||||
throw new Exception($customErrorMessage ?? "Too many requests. Please try again in {$err->secondsUntilAvailable} seconds.");
|
||||
} else {
|
||||
if ($err->getMessage() === 'This action is unauthorized.') {
|
||||
return redirect()->route('dashboard')->with('error', $customErrorMessage ?? $err->getMessage());
|
||||
return redirect()->route('dashboard')->with('error', $customErrorMessage ?? $err->getMessage());
|
||||
}
|
||||
throw new Exception($customErrorMessage ?? $err->getMessage());
|
||||
}
|
||||
@ -122,10 +122,11 @@ function generateSSHKey()
|
||||
$key = RSA::createKey();
|
||||
return [
|
||||
'private' => $key->toString('PKCS1'),
|
||||
'public' => $key->getPublicKey()->toString('OpenSSH',['comment' => 'coolify-generated-ssh-key'])
|
||||
'public' => $key->getPublicKey()->toString('OpenSSH', ['comment' => 'coolify-generated-ssh-key'])
|
||||
];
|
||||
}
|
||||
function formatPrivateKey(string $privateKey) {
|
||||
function formatPrivateKey(string $privateKey)
|
||||
{
|
||||
$privateKey = trim($privateKey);
|
||||
if (!str_ends_with($privateKey, "\n")) {
|
||||
$privateKey .= "\n";
|
||||
@ -140,30 +141,34 @@ function generate_application_name(string $git_repository, string $git_branch):
|
||||
|
||||
function is_transactional_emails_active(): bool
|
||||
{
|
||||
return data_get(InstanceSettings::get(), 'smtp_enabled');
|
||||
return isEmailEnabled(InstanceSettings::get());
|
||||
}
|
||||
|
||||
function set_transanctional_email_settings(InstanceSettings | null $settings = null): void
|
||||
function set_transanctional_email_settings(InstanceSettings | null $settings = null): string|null
|
||||
{
|
||||
if (!$settings) {
|
||||
$settings = InstanceSettings::get();
|
||||
}
|
||||
$password = data_get($settings, 'smtp_password');
|
||||
if (isset($password)) {
|
||||
$password = decrypt($password);
|
||||
if (data_get($settings, 'resend_enabled')) {
|
||||
config()->set('mail.default', 'resend');
|
||||
config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
|
||||
return 'resend';
|
||||
}
|
||||
|
||||
config()->set('mail.default', 'smtp');
|
||||
config()->set('mail.mailers.smtp', [
|
||||
"transport" => "smtp",
|
||||
"host" => data_get($settings, 'smtp_host'),
|
||||
"port" => data_get($settings, 'smtp_port'),
|
||||
"encryption" => data_get($settings, 'smtp_encryption'),
|
||||
"username" => data_get($settings, 'smtp_username'),
|
||||
"password" => $password,
|
||||
"timeout" => data_get($settings, 'smtp_timeout'),
|
||||
"local_domain" => null,
|
||||
]);
|
||||
if (data_get($settings, 'smtp_enabled')) {
|
||||
config()->set('mail.default', 'smtp');
|
||||
config()->set('mail.mailers.smtp', [
|
||||
"transport" => "smtp",
|
||||
"host" => data_get($settings, 'smtp_host'),
|
||||
"port" => data_get($settings, 'smtp_port'),
|
||||
"encryption" => data_get($settings, 'smtp_encryption'),
|
||||
"username" => data_get($settings, 'smtp_username'),
|
||||
"password" => data_get($settings, 'smtp_password'),
|
||||
"timeout" => data_get($settings, 'smtp_timeout'),
|
||||
"local_domain" => null,
|
||||
]);
|
||||
return 'smtp';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function base_ip(): string
|
||||
@ -246,7 +251,10 @@ function send_internal_notification(string $message): void
|
||||
function send_user_an_email(MailMessage $mail, string $email): void
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
set_transanctional_email_settings($settings);
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (!$type) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
@ -259,5 +267,9 @@ function send_user_an_email(MailMessage $mail, string $email): void
|
||||
->subject($mail->subject)
|
||||
->html((string) $mail->render())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
function isEmailEnabled($notifiable)
|
||||
{
|
||||
return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
|
||||
}
|
||||
|
@ -66,7 +66,6 @@ function isSubscriptionActive()
|
||||
return $subscription->stripe_invoice_paid === true && $subscription->stripe_cancel_at_period_end === false;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
function isSubscriptionOnGracePeriod()
|
||||
{
|
||||
@ -92,13 +91,16 @@ function subscriptionProvider()
|
||||
{
|
||||
return config('subscription.provider');
|
||||
}
|
||||
function isLemon () {
|
||||
function isLemon()
|
||||
{
|
||||
return config('subscription.provider') === 'lemon';
|
||||
}
|
||||
function isStripe() {
|
||||
function isStripe()
|
||||
{
|
||||
return config('subscription.provider') === 'stripe';
|
||||
}
|
||||
function isPaddle() {
|
||||
function isPaddle()
|
||||
{
|
||||
return config('subscription.provider') === 'paddle';
|
||||
}
|
||||
function getStripeCustomerPortalSession(Team $team)
|
||||
|
@ -11,11 +11,15 @@
|
||||
],
|
||||
'limits' => [
|
||||
'server' => [
|
||||
'zero' => 0,
|
||||
'self-hosted' => 999999999999,
|
||||
'basic' => 1,
|
||||
'pro' => 10,
|
||||
'ultimate' => 25,
|
||||
],
|
||||
'smtp' => [
|
||||
'email' => [
|
||||
'zero' => false,
|
||||
'self-hosted' => true,
|
||||
'basic' => false,
|
||||
'pro' => true,
|
||||
'ultimate' => true,
|
||||
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->boolean('resend_enabled')->default(false);
|
||||
$table->text('resend_api_key')->nullable();
|
||||
$table->boolean('use_instance_email_settings')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->dropColumn('resend_enabled');
|
||||
$table->dropColumn('resend_api_key');
|
||||
$table->dropColumn('use_instance_email_settings');
|
||||
});
|
||||
}
|
||||
};
|
@ -1,14 +1,13 @@
|
||||
Congratulations!<br>
|
||||
Congratulations!<br>
|
||||
<br>
|
||||
You have been invited to join the Coolify Cloud. <a href="{{base_url()}}/login">Login here</a>
|
||||
<br>
|
||||
<br>
|
||||
Credentials:
|
||||
<br>
|
||||
Email: {{ $email }}
|
||||
<br>
|
||||
Password: {{ $password }}
|
||||
Here is your initial login information.
|
||||
<br>
|
||||
Email: <br>
|
||||
{{ $email }}
|
||||
<br><br>
|
||||
Password:<br>
|
||||
{{ $password }}
|
||||
<br><br>
|
||||
(You will forced to change it on first login.)
|
||||
|
||||
|
@ -52,7 +52,7 @@ function changePasswordFieldType(event) {
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
Livewire.emit('message', 'Copied to clipboard.');
|
||||
Livewire.emit('success', 'Copied to clipboard.');
|
||||
}
|
||||
|
||||
Livewire.on('reloadWindow', (timeout) => {
|
||||
|
@ -16,59 +16,106 @@
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if (isInstanceAdmin())
|
||||
@if (isInstanceAdmin() && !$team->use_instance_email_settings)
|
||||
<x-forms.button wire:click='copyFromInstanceSettings'>
|
||||
Copy from Instance Settings
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($model->smtp_enabled)
|
||||
@if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings'))
|
||||
<x-forms.button onclick="sendTestEmail.showModal()"
|
||||
class="text-white normal-case btn btn-xs no-animation btn-primary">
|
||||
Send Test Email
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-48">
|
||||
<x-forms.checkbox instantSave id="model.smtp_enabled" label="Notification Enabled" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input id="model.smtp_recipients"
|
||||
placeholder="If empty, all users will be notified in the team."
|
||||
helper="Email list to send the all notifications to, separated by comma." label="Recipients" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input required id="model.smtp_host" helper="SMTP Hostname" placeholder="smtp.mailgun.org"
|
||||
label="Host" />
|
||||
<x-forms.input required id="model.smtp_port" helper="SMTP Port" placeholder="587" label="Port" />
|
||||
<x-forms.input helper="If SMTP through SSL, set it to 'tls'." placeholder="tls"
|
||||
id="model.smtp_encryption" label="Encryption" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input id="model.smtp_username" label="SMTP Username" />
|
||||
<x-forms.input type="password" id="model.smtp_password" label="SMTP Password" />
|
||||
<x-forms.input id="model.smtp_timeout" helper="Timeout value for sending emails." label="Timeout" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input required id="model.smtp_from_name" helper="Name used in emails." label="From Name" />
|
||||
<x-forms.input required id="model.smtp_from_address" helper="Email address used in emails."
|
||||
label="From Address" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
@if (data_get($model, 'smtp_enabled'))
|
||||
<h4 class="mt-4">Subscribe to events</h4>
|
||||
@if ($this->sharedEmailEnabled)
|
||||
<div class="w-64 pb-4">
|
||||
<x-forms.checkbox instantSave="instantSaveInstance" id="team.use_instance_email_settings"
|
||||
label="Use hosted email service" />
|
||||
</div>
|
||||
@endif
|
||||
@if (!$team->use_instance_email_settings)
|
||||
<form class="flex flex-col items-end gap-2 pb-4 xl:flex-row" wire:submit.prevent='submitFromFields'>
|
||||
<x-forms.input required id="team.smtp_from_name" helper="Name used in emails." label="From Name" />
|
||||
<x-forms.input required id="team.smtp_from_address" helper="Email address used in emails."
|
||||
label="From Address" />
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</form>
|
||||
<div class="flex flex-col gap-4">
|
||||
<details class="border rounded collapse border-coolgray-500 collapse-arrow ">
|
||||
<summary class="text-xl collapse-title">
|
||||
<div>SMTP Server</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave id="team.smtp_enabled" label="Enabled" />
|
||||
</div>
|
||||
</summary>
|
||||
<div class="collapse-content">
|
||||
<form wire:submit.prevent='submit' class="flex flex-col">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input required id="team.smtp_host" placeholder="smtp.mailgun.org"
|
||||
label="Host" />
|
||||
<x-forms.input required id="team.smtp_port" placeholder="587" label="Port" />
|
||||
<x-forms.input id="team.smtp_encryption" helper="If SMTP uses SSL, set it to 'tls'."
|
||||
placeholder="tls" label="Encryption" />
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input id="team.smtp_username" label="SMTP Username" />
|
||||
<x-forms.input id="team.smtp_password" type="password" label="SMTP Password" />
|
||||
<x-forms.input id="team.smtp_timeout" helper="Timeout value for sending emails."
|
||||
label="Timeout" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-4 pt-6">
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
<details class="border rounded collapse border-coolgray-500 collapse-arrow">
|
||||
<summary class="text-xl collapse-title">
|
||||
<div>Resend</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave='instantSaveResend' id="team.resend_enabled" label="Enabled" />
|
||||
</div>
|
||||
</summary>
|
||||
<div class="collapse-content">
|
||||
<form wire:submit.prevent='submitResend' class="flex flex-col">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input type="password" id="team.resend_api_key" placeholder="API key"
|
||||
label="Host" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-4 pt-6">
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
@endif
|
||||
@if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings'))
|
||||
<h3 class="mt-4">Subscribe to events</h3>
|
||||
<div class="w-64">
|
||||
@if (isDev())
|
||||
<x-forms.checkbox instantSave="saveModel" id="model.smtp_notifications_test" label="Test" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_test" label="Test" />
|
||||
@endif
|
||||
<h4 class="mt-4">General</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="model.smtp_notifications_status_changes"
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_status_changes"
|
||||
label="Container Status Changes" />
|
||||
<h4 class="mt-4">Applications</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="model.smtp_notifications_deployments" label="Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_deployments" label="Deployments" />
|
||||
<h4 class="mt-4">Databases</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="model.smtp_notifications_database_backups"
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_database_backups"
|
||||
label="Backup Statuses" />
|
||||
</div>
|
||||
@endif
|
||||
|
@ -11,9 +11,9 @@
|
||||
</form>
|
||||
</dialog>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Transactional Emails</h2>
|
||||
<h2>Transactional/Shared Email</h2>
|
||||
</div>
|
||||
<div class="pb-4 ">SMTP settings for password resets, invitations, etc.</div>
|
||||
<div class="pb-4 ">Email settings for password resets, invitations, shared with Pro+ subscribers etc.</div>
|
||||
<form wire:submit.prevent='submitFromFields' class="pb-4">
|
||||
<div class="flex flex-col items-end w-full gap-2 xl:flex-row">
|
||||
<x-forms.input required id="settings.smtp_from_name" helper="Name used in emails." label="From Name" />
|
||||
@ -63,11 +63,11 @@ class="text-white normal-case btn btn-xs no-animation btn-primary">
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
<details class="border rounded collapse border-coolgray-500 collapse-arrow ">
|
||||
<details class="border rounded collapse border-coolgray-500 collapse-arrow">
|
||||
<summary class="text-xl collapse-title">
|
||||
<div>Resend</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave='instantSaveResend' id="settings.resend_enabled" label="Enabled" />
|
||||
<x-forms.checkbox instantSave='submitResend' id="settings.resend_enabled" label="Enabled" />
|
||||
</div>
|
||||
</summary>
|
||||
<div class="collapse-content">
|
||||
|
@ -5,9 +5,9 @@
|
||||
<a :class="activeTab === 'general' && 'text-white'"
|
||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
||||
<a :class="activeTab === 'backup' && 'text-white'"
|
||||
@click.prevent="activeTab = 'backup'; window.location.hash = 'backup'" href="#">Backup</a>
|
||||
@click.prevent="activeTab = 'backup'; window.location.hash = 'backup'" href="#">Instance Backup</a>
|
||||
<a :class="activeTab === 'smtp' && 'text-white'"
|
||||
@click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">SMTP</a>
|
||||
@click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">Transactional/Shared Email</a>
|
||||
</div>
|
||||
<div class="w-full pl-8">
|
||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||
|
@ -12,7 +12,7 @@
|
||||
</div>
|
||||
<div class="w-full pl-8">
|
||||
<div x-cloak x-show="activeTab === 'email'" class="h-full">
|
||||
<livewire:notifications.email-settings :model="auth()
|
||||
<livewire:notifications.email-settings :team="auth()
|
||||
->user()
|
||||
->currentTeam()" />
|
||||
</div>
|
||||
|
@ -28,7 +28,10 @@
|
||||
|
||||
Route::post('/forgot-password', function (Request $request) {
|
||||
if (is_transactional_emails_active()) {
|
||||
set_transanctional_email_settings();
|
||||
$type = set_transanctional_email_settings();
|
||||
if (!$type) {
|
||||
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||
}
|
||||
$request->validate([Fortify::email() => 'required|email']);
|
||||
$status = Password::broker(config('fortify.passwords'))->sendResetLink(
|
||||
$request->only(Fortify::email())
|
||||
|
Loading…
Reference in New Issue
Block a user