Files
coolify/app/Models/InstanceSettings.php
T
Andras Bacsai a5b4e1d32e feat(email): add a separate notification sender to the instance email settings
Team notifications sent through the instance email settings, the S3
connection error alert, and the channel disabled alert now use the
optional notification from address and name. Account mails keep the
default sender. Remove the unused emails command and its views.
2026-11-03 21:19:50 +01:00

266 lines
8.0 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Once;
use Spatie\Url\Url;
class InstanceSettings extends Model
{
protected $attributes = [
'is_dashboard_force_https_enabled' => true,
];
protected $fillable = [
'public_ipv4',
'public_ipv6',
'fqdn',
'public_port_min',
'public_port_max',
'do_not_track',
'is_auto_update_enabled',
'is_backup_before_update_enabled',
'is_registration_enabled',
'disable_registration_when_oauth_enabled',
'next_channel',
'smtp_enabled',
'smtp_from_address',
'smtp_from_name',
'notification_from_address',
'notification_from_name',
'smtp_recipients',
'smtp_host',
'smtp_port',
'smtp_encryption',
'smtp_username',
'smtp_password',
'smtp_timeout',
'smtp_ehlo_domain',
'resend_enabled',
'resend_api_key',
'is_dns_validation_enabled',
'custom_dns_servers',
'domain_connect_private_key',
'instance_name',
'is_api_enabled',
'allowed_ips',
'auto_update_frequency',
'update_check_frequency',
'update_channel',
'update_custom_tag',
'previous_update_channel',
'new_version_available',
'instance_timezone',
'helper_version',
'disable_two_step_confirmation',
'is_sponsorship_popup_enabled',
'dev_helper_version',
'is_wire_navigate_enabled',
'is_mcp_server_enabled',
'webhook_allowed_internal_hosts',
'webhook_allow_localhost',
'avatar_storage_type',
'avatar_s3_storage_id',
'image_cdn_url',
'file_download_s3_storage_id',
'is_dashboard_force_https_enabled',
'is_passkey_enabled',
];
protected $hidden = [
'smtp_from_address',
'smtp_from_name',
'notification_from_address',
'notification_from_name',
'smtp_recipients',
'smtp_host',
'smtp_username',
'smtp_password',
'resend_api_key',
'domain_connect_private_key',
'sentinel_token',
];
protected $casts = [
'smtp_enabled' => 'boolean',
'smtp_from_address' => 'encrypted',
'smtp_from_name' => 'encrypted',
'notification_from_address' => 'encrypted',
'notification_from_name' => 'encrypted',
'smtp_recipients' => 'encrypted',
'smtp_host' => 'encrypted',
'smtp_port' => 'integer',
'smtp_username' => 'encrypted',
'smtp_password' => 'encrypted',
'smtp_timeout' => 'integer',
'resend_enabled' => 'boolean',
'resend_api_key' => 'encrypted',
'domain_connect_private_key' => 'encrypted',
'allowed_ip_ranges' => 'array',
'is_auto_update_enabled' => 'boolean',
'is_backup_before_update_enabled' => 'boolean',
'is_registration_enabled' => 'boolean',
'disable_registration_when_oauth_enabled' => 'boolean',
'auto_update_frequency' => 'string',
'update_check_frequency' => 'string',
'sentinel_token' => 'encrypted',
'is_wire_navigate_enabled' => 'boolean',
'is_mcp_server_enabled' => 'boolean',
'webhook_allowed_internal_hosts' => 'array',
'webhook_allow_localhost' => 'boolean',
'is_dashboard_force_https_enabled' => 'boolean',
'is_passkey_enabled' => 'boolean',
'server_validation_rate_limit_seconds' => 'integer',
'server_validation_max_failures_per_ip' => 'integer',
'server_validation_max_failures_per_team' => 'integer',
];
protected static function booted(): void
{
static::creating(function ($settings) {
if (blank($settings->getAttributes()['auto_update_frequency'] ?? null)) {
$settings->auto_update_frequency = self::randomAutoUpdateFrequency();
}
});
static::created(function () {
Once::flush();
});
static::updated(function ($settings) {
// Clear once() cache so subsequent calls get fresh data
Once::flush();
if ($settings->wasChanged(['fqdn', 'public_ipv4', 'public_ipv6'])) {
ServerSetting::followInstanceUrlChange(
ServerSetting::instanceSentinelUrl($settings->getOriginal('fqdn'), $settings->getOriginal('public_ipv4'), $settings->getOriginal('public_ipv6')),
ServerSetting::instanceSentinelUrl($settings->fqdn, $settings->public_ipv4, $settings->public_ipv6),
);
}
});
}
/**
* Daily auto update at a random minute after midnight, so instances do not all pull images from the registry in the same second.
*/
public static function randomAutoUpdateFrequency(): string
{
return random_int(0, 59).' 0 * * *';
}
/**
* After a switch to a more stable channel, the instance keeps the updates of the channel it left until the stable release of its version is out.
*/
public function switchUpdateChannel(string $channel): void
{
$from = $this->getOriginal('update_channel');
$this->update_channel = $channel;
if ($channel === $from) {
return;
}
$channels = ['stable', 'preview', 'nightly'];
$fromRank = array_search($from, $channels, true);
$toRank = array_search($channel, $channels, true);
$followed = $this->getOriginal('previous_update_channel');
$this->previous_update_channel = match (true) {
$fromRank === false || $toRank === false || $toRank >= $fromRank => null,
array_search($followed, $channels, true) > $fromRank => $followed,
default => $from,
};
}
public function isPasswordRegistrationAllowed(): bool
{
if (! $this->is_registration_enabled) {
return false;
}
if (! $this->disable_registration_when_oauth_enabled) {
return true;
}
return ! OauthSetting::where('enabled', true)->exists();
}
public function fqdn(): Attribute
{
return Attribute::make(
set: function ($value) {
if ($value) {
$url = Url::fromString($value);
$host = $url->getHost();
return $url->getScheme().'://'.$host;
}
}
);
}
public function updateCheckFrequency(): Attribute
{
return Attribute::make(
set: function ($value) {
return translate_cron_expression($value);
},
get: function ($value) {
return translate_cron_expression($value);
}
);
}
public function autoUpdateFrequency(): Attribute
{
return Attribute::make(
set: function ($value) {
return translate_cron_expression($value);
},
get: function ($value) {
return translate_cron_expression($value);
}
);
}
public static function get()
{
return once(fn () => InstanceSettings::findOrFail(0));
}
// public function getRecipients($notification)
// {
// $recipients = data_get($notification, 'emails', null);
// if (is_null($recipients) || $recipients === '') {
// return [];
// }
// return explode(',', $recipients);
// }
public function getTitleDisplayName(): string
{
$instanceName = $this->instance_name;
if (! $instanceName) {
return '';
}
return "[{$instanceName}]";
}
// public function helperVersion(): Attribute
// {
// return Attribute::make(
// get: function ($value) {
// if (isDev()) {
// return 'latest';
// }
// return $value;
// }
// );
// }
}