feat(ui): Shadow UI redesign, domain management, and DNS autoconfigure (#11119)

Co-authored-by: ShadowArcanist <162910371+ShadowArcanist@users.noreply.github.com>
This commit is contained in:
Andras BacsaiandShadowArcanist authored and GitHub committed 2026-08-03 23:11:54 +02:00
1 parent 0b843bb07c
commit 8ae56587d5
497 files changed
+44045 -22630

No files matched your search

+93 -5
View File
@@ -181,6 +181,7 @@ class Application extends BaseModel
'docker_compose',
'docker_compose_raw',
'docker_compose_domains',
'domain_dns_statuses',
'docker_compose_custom_start_command',
'docker_compose_custom_build_command',
'swarm_replicas',
@@ -233,6 +234,7 @@ class Application extends BaseModel
'docker_compose',
'docker_compose_raw',
'custom_labels',
'domain_dns_statuses',
];
protected function casts(): array
@@ -243,6 +245,7 @@ class Application extends BaseModel
'manual_webhook_secret_gitlab' => 'encrypted',
'manual_webhook_secret_bitbucket' => 'encrypted',
'manual_webhook_secret_gitea' => 'encrypted',
'domain_dns_statuses' => 'array',
'restart_count' => 'integer',
'max_restart_count' => 'integer',
'last_restart_at' => 'datetime',
@@ -1097,16 +1100,101 @@ class Application extends BaseModel
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
}
public function deployments(int $skip = 0, int $take = 10, ?string $pullRequestId = null)
{
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->orderBy('created_at', 'desc');
/**
* @return array{count: int, deployments: Collection<int, ApplicationDeploymentQueue>}
*/
public function deployments(
int $skip = 0,
int $take = 10,
?string $pullRequestId = null,
?string $search = null,
string $filter = 'all',
string $sort = 'newest',
): array {
$deployments = ApplicationDeploymentQueue::query()
->where('application_id', $this->id);
if ($pullRequestId) {
$deployments = $deployments->where('pull_request_id', $pullRequestId);
$deployments->where('pull_request_id', $pullRequestId);
}
$search = trim((string) $search);
if ($search !== '') {
$normalizedSearch = Str::lower($search);
$statusAliases = [
'success' => ApplicationDeploymentStatus::FINISHED->value,
'in progress' => ApplicationDeploymentStatus::IN_PROGRESS->value,
'cancelled' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
];
$deployments->where(function ($query) use ($search, $normalizedSearch, $statusAliases) {
$query
->whereLike('deployment_uuid', "%{$search}%")
->orWhereLike('commit', "%{$search}%")
->orWhereLike('commit_message', "%{$search}%")
->orWhereLike('server_name', "%{$search}%")
->orWhereLike('status', "%{$search}%");
if (isset($statusAliases[$normalizedSearch])) {
$query->orWhere('status', $statusAliases[$normalizedSearch]);
}
if (is_numeric($search) && (int) $search > 0) {
$query->orWhere('pull_request_id', (int) $search);
}
match ($normalizedSearch) {
'pull request', 'pull requests', 'pr' => $query->orWhere('pull_request_id', '>', 0),
'webhook', 'webhooks' => $query->orWhere('is_webhook', true),
'rollback', 'rollbacks' => $query->orWhere('rollback', true),
'api' => $query->orWhere('is_api', true),
'manual' => $query->orWhere(function ($sourceQuery) {
$sourceQuery
->where('pull_request_id', '<=', 0)
->where('is_webhook', false)
->where('rollback', false)
->where('is_api', false);
}),
default => null,
};
});
}
if (Str::startsWith($filter, 'status:')) {
$deployments->where('status', Str::after($filter, 'status:'));
}
if (Str::startsWith($filter, 'source:')) {
match (Str::after($filter, 'source:')) {
'pull-request' => $deployments->where('pull_request_id', '>', 0),
'webhook' => $deployments
->where('pull_request_id', '<=', 0)
->where('is_webhook', true),
'rollback' => $deployments
->where('pull_request_id', '<=', 0)
->where('is_webhook', false)
->where('rollback', true),
'api' => $deployments
->where('pull_request_id', '<=', 0)
->where('is_webhook', false)
->where('rollback', false)
->where('is_api', true),
'manual' => $deployments
->where('pull_request_id', '<=', 0)
->where('is_webhook', false)
->where('rollback', false)
->where('is_api', false),
default => null,
};
}
$count = $deployments->count();
$deployments = $deployments->skip($skip)->take($take)->get();
$deployments = $deployments
->orderBy('created_at', $sort === 'oldest' ? 'asc' : 'desc')
->orderBy('id', $sort === 'oldest' ? 'asc' : 'desc')
->skip($skip)
->take($take)
->get();
return [
'count' => $count,
+3
View File
@@ -33,6 +33,7 @@ class InstanceSettings extends Model
'resend_api_key',
'is_dns_validation_enabled',
'custom_dns_servers',
'domain_connect_private_key',
'instance_name',
'is_api_enabled',
'allowed_ips',
@@ -58,6 +59,7 @@ class InstanceSettings extends Model
'smtp_username',
'smtp_password',
'resend_api_key',
'domain_connect_private_key',
'sentinel_token',
];
@@ -74,6 +76,7 @@ class InstanceSettings extends Model
'resend_enabled' => 'boolean',
'resend_api_key' => 'encrypted',
'domain_connect_private_key' => 'encrypted',
'allowed_ip_ranges' => 'array',
'is_auto_update_enabled' => 'boolean',
+18
View File
@@ -17,6 +17,8 @@ class ServiceApplication extends BaseModel
'human_name',
'description',
'fqdn',
'redirect',
'domain_dns_statuses',
'ports',
'exposes',
'status',
@@ -31,6 +33,22 @@ class ServiceApplication extends BaseModel
'is_migrated',
];
/**
* Internal DNS check cache — not part of the public API surface.
*
* @var list<string>
*/
protected $hidden = [
'domain_dns_statuses',
];
protected function casts(): array
{
return [
'domain_dns_statuses' => 'array',
];
}
protected static function booted()
{
static::deleting(function ($service) {
+5 -1
View File
@@ -8,8 +8,8 @@ use App\Jobs\V5TeardownTeamJob;
use App\Notifications\Channels\SendsDiscord;
use App\Notifications\Channels\SendsEmail;
use App\Notifications\Channels\SendsPushover;
use App\Support\V5\V5Feature;
use App\Notifications\Channels\SendsSlack;
use App\Support\V5\V5Feature;
use App\Traits\HasNotificationSettings;
use App\Traits\HasSafeStringAttribute;
use Illuminate\Database\Eloquent\Casts\Attribute;
@@ -52,6 +52,10 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
'is_mcp_server_enabled',
];
protected $attributes = [
'is_mcp_server_enabled' => true,
];
protected $casts = [
'personal_team' => 'boolean',
'is_mcp_server_enabled' => 'boolean',