Skip to content

Commit 25c0c88

Browse files
committed
Merge remote-tracking branch 'origin/next' into feat/refresh-repos
2 parents f09115a + 2692496 commit 25c0c88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+563
-95
lines changed

app/Http/Controllers/Api/DatabasesController.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ public function update_by_uuid(Request $request)
643643
'database_backup_retention_amount_s3' => ['type' => 'integer', 'description' => 'Number of backups to retain in S3'],
644644
'database_backup_retention_days_s3' => ['type' => 'integer', 'description' => 'Number of days to retain backups in S3'],
645645
'database_backup_retention_max_storage_s3' => ['type' => 'integer', 'description' => 'Max storage (MB) for S3 backups'],
646+
'timeout' => ['type' => 'integer', 'description' => 'Backup job timeout in seconds (min: 60, max: 36000)', 'default' => 3600],
646647
],
647648
),
648649
)
@@ -679,7 +680,7 @@ public function update_by_uuid(Request $request)
679680
)]
680681
public function create_backup(Request $request)
681682
{
682-
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid'];
683+
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid', 'timeout'];
683684

684685
$teamId = getTeamIdFromToken();
685686
if (is_null($teamId)) {
@@ -706,6 +707,7 @@ public function create_backup(Request $request)
706707
'database_backup_retention_amount_s3' => 'integer|min:0',
707708
'database_backup_retention_days_s3' => 'integer|min:0',
708709
'database_backup_retention_max_storage_s3' => 'integer|min:0',
710+
'timeout' => 'integer|min:60|max:36000',
709711
]);
710712

711713
if ($validator->fails()) {
@@ -880,6 +882,7 @@ public function create_backup(Request $request)
880882
'database_backup_retention_amount_s3' => ['type' => 'integer', 'description' => 'Retention amount of the backup in s3'],
881883
'database_backup_retention_days_s3' => ['type' => 'integer', 'description' => 'Retention days of the backup in s3'],
882884
'database_backup_retention_max_storage_s3' => ['type' => 'integer', 'description' => 'Max storage of the backup in S3'],
885+
'timeout' => ['type' => 'integer', 'description' => 'Backup job timeout in seconds (min: 60, max: 36000)', 'default' => 3600],
883886
],
884887
),
885888
)
@@ -909,7 +912,7 @@ public function create_backup(Request $request)
909912
)]
910913
public function update_backup(Request $request)
911914
{
912-
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid'];
915+
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid', 'timeout'];
913916

914917
$teamId = getTeamIdFromToken();
915918
if (is_null($teamId)) {
@@ -927,13 +930,14 @@ public function update_backup(Request $request)
927930
'dump_all' => 'boolean',
928931
's3_storage_uuid' => 'string|exists:s3_storages,uuid|nullable',
929932
'databases_to_backup' => 'string|nullable',
930-
'frequency' => 'string|in:every_minute,hourly,daily,weekly,monthly,yearly',
933+
'frequency' => 'string',
931934
'database_backup_retention_amount_locally' => 'integer|min:0',
932935
'database_backup_retention_days_locally' => 'integer|min:0',
933936
'database_backup_retention_max_storage_locally' => 'integer|min:0',
934937
'database_backup_retention_amount_s3' => 'integer|min:0',
935938
'database_backup_retention_days_s3' => 'integer|min:0',
936939
'database_backup_retention_max_storage_s3' => 'integer|min:0',
940+
'timeout' => 'integer|min:60|max:36000',
937941
]);
938942
if ($validator->fails()) {
939943
return response()->json([
@@ -960,6 +964,17 @@ public function update_backup(Request $request)
960964

961965
$this->authorize('update', $database);
962966

967+
// Validate frequency is a valid cron expression
968+
if ($request->filled('frequency')) {
969+
$isValid = validate_cron_expression($request->frequency);
970+
if (! $isValid) {
971+
return response()->json([
972+
'message' => 'Validation failed.',
973+
'errors' => ['frequency' => ['Invalid cron expression or frequency format.']],
974+
], 422);
975+
}
976+
}
977+
963978
if ($request->boolean('save_s3') && ! $request->filled('s3_storage_uuid')) {
964979
return response()->json([
965980
'message' => 'Validation failed.',

app/Http/Controllers/Api/ServersController.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,11 @@ public function create_server(Request $request)
598598
'is_build_server' => ['type' => 'boolean', 'description' => 'Is build server.'],
599599
'instant_validate' => ['type' => 'boolean', 'description' => 'Instant validate.'],
600600
'proxy_type' => ['type' => 'string', 'enum' => ['traefik', 'caddy', 'none'], 'description' => 'The proxy type.'],
601+
'concurrent_builds' => ['type' => 'integer', 'description' => 'Number of concurrent builds.'],
602+
'dynamic_timeout' => ['type' => 'integer', 'description' => 'Deployment timeout in seconds.'],
603+
'deployment_queue_limit' => ['type' => 'integer', 'description' => 'Maximum number of queued deployments.'],
604+
'server_disk_usage_notification_threshold' => ['type' => 'integer', 'description' => 'Server disk usage notification threshold (%).'],
605+
'server_disk_usage_check_frequency' => ['type' => 'string', 'description' => 'Cron expression for disk usage check frequency.'],
601606
],
602607
),
603608
),
@@ -634,7 +639,7 @@ public function create_server(Request $request)
634639
)]
635640
public function update_server(Request $request)
636641
{
637-
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate', 'proxy_type'];
642+
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate', 'proxy_type', 'concurrent_builds', 'dynamic_timeout', 'deployment_queue_limit', 'server_disk_usage_notification_threshold', 'server_disk_usage_check_frequency'];
638643

639644
$teamId = getTeamIdFromToken();
640645
if (is_null($teamId)) {
@@ -655,6 +660,11 @@ public function update_server(Request $request)
655660
'is_build_server' => 'boolean|nullable',
656661
'instant_validate' => 'boolean|nullable',
657662
'proxy_type' => 'string|nullable',
663+
'concurrent_builds' => 'integer|min:1',
664+
'dynamic_timeout' => 'integer|min:1',
665+
'deployment_queue_limit' => 'integer|min:1',
666+
'server_disk_usage_notification_threshold' => 'integer|min:1|max:100',
667+
'server_disk_usage_check_frequency' => 'string',
658668
]);
659669

660670
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
@@ -691,6 +701,19 @@ public function update_server(Request $request)
691701
'is_build_server' => $request->is_build_server,
692702
]);
693703
}
704+
705+
if ($request->has('server_disk_usage_check_frequency') && ! validate_cron_expression($request->server_disk_usage_check_frequency)) {
706+
return response()->json([
707+
'message' => 'Validation failed.',
708+
'errors' => ['server_disk_usage_check_frequency' => ['Invalid Cron / Human expression for Disk Usage Check Frequency.']],
709+
], 422);
710+
}
711+
712+
$advancedSettings = $request->only(['concurrent_builds', 'dynamic_timeout', 'deployment_queue_limit', 'server_disk_usage_notification_threshold', 'server_disk_usage_check_frequency']);
713+
if (! empty($advancedSettings)) {
714+
$server->settings()->update(array_filter($advancedSettings, fn ($value) => ! is_null($value)));
715+
}
716+
694717
if ($request->instant_validate) {
695718
ValidateServer::dispatch($server);
696719
}

app/Livewire/Notifications/Email.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Email extends Component
4242
public ?string $smtpHost = null;
4343

4444
#[Validate(['nullable', 'numeric', 'min:1', 'max:65535'])]
45-
public ?int $smtpPort = null;
45+
public ?string $smtpPort = null;
4646

4747
#[Validate(['nullable', 'string', 'in:starttls,tls,none'])]
4848
public ?string $smtpEncryption = null;
@@ -54,7 +54,7 @@ class Email extends Component
5454
public ?string $smtpPassword = null;
5555

5656
#[Validate(['nullable', 'numeric'])]
57-
public ?int $smtpTimeout = null;
57+
public ?string $smtpTimeout = null;
5858

5959
#[Validate(['boolean'])]
6060
public bool $resendEnabled = false;

app/Livewire/Project/Application/General.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ protected function rules(): array
153153
'staticImage' => 'required',
154154
'baseDirectory' => array_merge(['required'], array_slice(ValidationPatterns::directoryPathRules(), 1)),
155155
'publishDirectory' => ValidationPatterns::directoryPathRules(),
156-
'portsExposes' => 'required',
157-
'portsMappings' => 'nullable',
156+
'portsExposes' => ['required', 'string', 'regex:/^(\d+)(,\d+)*$/'],
157+
'portsMappings' => ValidationPatterns::portMappingRules(),
158158
'customNetworkAliases' => 'nullable',
159159
'dockerfile' => 'nullable',
160160
'dockerRegistryImageName' => 'nullable',
@@ -212,6 +212,8 @@ protected function messages(): array
212212
'staticImage.required' => 'The Static Image field is required.',
213213
'baseDirectory.required' => 'The Base Directory field is required.',
214214
'portsExposes.required' => 'The Exposed Ports field is required.',
215+
'portsExposes.regex' => 'Ports exposes must be a comma-separated list of port numbers (e.g. 3000,3001).',
216+
...ValidationPatterns::portMappingMessages(),
215217
'isStatic.required' => 'The Static setting is required.',
216218
'isStatic.boolean' => 'The Static setting must be true or false.',
217219
'isSpa.required' => 'The SPA setting is required.',
@@ -756,6 +758,12 @@ public function submit($showToaster = true)
756758
$this->authorize('update', $this->application);
757759

758760
$this->resetErrorBag();
761+
762+
$this->portsExposes = str($this->portsExposes)->replace(' ', '')->trim()->toString();
763+
if ($this->portsMappings) {
764+
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
765+
}
766+
759767
$this->validate();
760768

761769
$oldPortsExposes = $this->application->ports_exposes;

app/Livewire/Project/Database/BackupEdit.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class BackupEdit extends Component
7676
public bool $dumpAll = false;
7777

7878
#[Validate(['required', 'int', 'min:60', 'max:36000'])]
79-
public int $timeout = 3600;
79+
public int|string $timeout = 3600;
8080

8181
public function mount()
8282
{

app/Livewire/Project/Database/Clickhouse/General.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ protected function rules(): array
7979
'clickhouseAdminUser' => 'required|string',
8080
'clickhouseAdminPassword' => 'required|string',
8181
'image' => 'required|string',
82-
'portsMappings' => 'nullable|string',
82+
'portsMappings' => ValidationPatterns::portMappingRules(),
8383
'isPublic' => 'nullable|boolean',
8484
'publicPort' => 'nullable|integer|min:1|max:65535',
8585
'publicPortTimeout' => 'nullable|integer|min:1',
@@ -94,6 +94,7 @@ protected function messages(): array
9494
{
9595
return array_merge(
9696
ValidationPatterns::combinedMessages(),
97+
ValidationPatterns::portMappingMessages(),
9798
[
9899
'clickhouseAdminUser.required' => 'The Admin User field is required.',
99100
'clickhouseAdminUser.string' => 'The Admin User must be a string.',
@@ -209,6 +210,9 @@ public function submit()
209210
try {
210211
$this->authorize('update', $this->database);
211212

213+
if ($this->portsMappings) {
214+
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
215+
}
212216
if (str($this->publicPort)->isEmpty()) {
213217
$this->publicPort = null;
214218
}

app/Livewire/Project/Database/Dragonfly/General.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ public function getListeners()
5757

5858
return [
5959
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
60-
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
60+
"echo-private:user.{$userId},DatabaseStatusChanged" => 'refresh',
61+
"echo-private:team.{$teamId},ServiceChecked" => 'refresh',
6162
];
6263
}
6364

@@ -90,7 +91,7 @@ protected function rules(): array
9091
'description' => ValidationPatterns::descriptionRules(),
9192
'dragonflyPassword' => 'required|string',
9293
'image' => 'required|string',
93-
'portsMappings' => 'nullable|string',
94+
'portsMappings' => ValidationPatterns::portMappingRules(),
9495
'isPublic' => 'nullable|boolean',
9596
'publicPort' => 'nullable|integer|min:1|max:65535',
9697
'publicPortTimeout' => 'nullable|integer|min:1',
@@ -106,6 +107,7 @@ protected function messages(): array
106107
{
107108
return array_merge(
108109
ValidationPatterns::combinedMessages(),
110+
ValidationPatterns::portMappingMessages(),
109111
[
110112
'dragonflyPassword.required' => 'The Dragonfly Password field is required.',
111113
'dragonflyPassword.string' => 'The Dragonfly Password must be a string.',
@@ -219,6 +221,9 @@ public function submit()
219221
try {
220222
$this->authorize('update', $this->database);
221223

224+
if ($this->portsMappings) {
225+
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
226+
}
222227
if (str($this->publicPort)->isEmpty()) {
223228
$this->publicPort = null;
224229
}
@@ -295,4 +300,10 @@ public function regenerateSslCertificate()
295300
handleError($e, $this);
296301
}
297302
}
303+
304+
public function refresh(): void
305+
{
306+
$this->database->refresh();
307+
$this->syncData();
308+
}
298309
}

app/Livewire/Project/Database/Keydb/General.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ public function getListeners()
5959

6060
return [
6161
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
62-
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
62+
"echo-private:user.{$userId},DatabaseStatusChanged" => 'refresh',
63+
"echo-private:team.{$teamId},ServiceChecked" => 'refresh',
6364
];
6465
}
6566

@@ -93,7 +94,7 @@ protected function rules(): array
9394
'keydbConf' => 'nullable|string',
9495
'keydbPassword' => 'required|string',
9596
'image' => 'required|string',
96-
'portsMappings' => 'nullable|string',
97+
'portsMappings' => ValidationPatterns::portMappingRules(),
9798
'isPublic' => 'nullable|boolean',
9899
'publicPort' => 'nullable|integer|min:1|max:65535',
99100
'publicPortTimeout' => 'nullable|integer|min:1',
@@ -111,6 +112,7 @@ protected function messages(): array
111112
{
112113
return array_merge(
113114
ValidationPatterns::combinedMessages(),
115+
ValidationPatterns::portMappingMessages(),
114116
[
115117
'keydbPassword.required' => 'The KeyDB Password field is required.',
116118
'keydbPassword.string' => 'The KeyDB Password must be a string.',
@@ -226,6 +228,9 @@ public function submit()
226228
try {
227229
$this->authorize('manageEnvironment', $this->database);
228230

231+
if ($this->portsMappings) {
232+
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
233+
}
229234
if (str($this->publicPort)->isEmpty()) {
230235
$this->publicPort = null;
231236
}
@@ -300,4 +305,10 @@ public function regenerateSslCertificate()
300305
handleError($e, $this);
301306
}
302307
}
308+
309+
public function refresh(): void
310+
{
311+
$this->database->refresh();
312+
$this->syncData();
313+
}
303314
}

app/Livewire/Project/Database/Mariadb/General.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ class General extends Component
6161
public function getListeners()
6262
{
6363
$userId = Auth::id();
64+
$teamId = Auth::user()->currentTeam()->id;
6465

6566
return [
66-
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
67+
"echo-private:user.{$userId},DatabaseStatusChanged" => 'refresh',
68+
"echo-private:team.{$teamId},ServiceChecked" => 'refresh',
6769
];
6870
}
6971

@@ -78,7 +80,7 @@ protected function rules(): array
7880
'mariadbDatabase' => 'required',
7981
'mariadbConf' => 'nullable',
8082
'image' => 'required',
81-
'portsMappings' => 'nullable',
83+
'portsMappings' => ValidationPatterns::portMappingRules(),
8284
'isPublic' => 'nullable|boolean',
8385
'publicPort' => 'nullable|integer|min:1|max:65535',
8486
'publicPortTimeout' => 'nullable|integer|min:1',
@@ -92,6 +94,7 @@ protected function messages(): array
9294
{
9395
return array_merge(
9496
ValidationPatterns::combinedMessages(),
97+
ValidationPatterns::portMappingMessages(),
9598
[
9699
'name.required' => 'The Name field is required.',
97100
'mariadbRootPassword.required' => 'The Root Password field is required.',
@@ -215,6 +218,9 @@ public function submit()
215218
try {
216219
$this->authorize('update', $this->database);
217220

221+
if ($this->portsMappings) {
222+
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
223+
}
218224
if (str($this->publicPort)->isEmpty()) {
219225
$this->publicPort = null;
220226
}

app/Livewire/Project/Database/Mongodb/General.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ class General extends Component
6161
public function getListeners()
6262
{
6363
$userId = Auth::id();
64+
$teamId = Auth::user()->currentTeam()->id;
6465

6566
return [
66-
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
67+
"echo-private:user.{$userId},DatabaseStatusChanged" => 'refresh',
68+
"echo-private:team.{$teamId},ServiceChecked" => 'refresh',
6769
];
6870
}
6971

@@ -77,7 +79,7 @@ protected function rules(): array
7779
'mongoInitdbRootPassword' => 'required',
7880
'mongoInitdbDatabase' => 'required',
7981
'image' => 'required',
80-
'portsMappings' => 'nullable',
82+
'portsMappings' => ValidationPatterns::portMappingRules(),
8183
'isPublic' => 'nullable|boolean',
8284
'publicPort' => 'nullable|integer|min:1|max:65535',
8385
'publicPortTimeout' => 'nullable|integer|min:1',
@@ -92,6 +94,7 @@ protected function messages(): array
9294
{
9395
return array_merge(
9496
ValidationPatterns::combinedMessages(),
97+
ValidationPatterns::portMappingMessages(),
9598
[
9699
'name.required' => 'The Name field is required.',
97100
'mongoInitdbRootUsername.required' => 'The Root Username field is required.',
@@ -215,6 +218,9 @@ public function submit()
215218
try {
216219
$this->authorize('update', $this->database);
217220

221+
if ($this->portsMappings) {
222+
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
223+
}
218224
if (str($this->publicPort)->isEmpty()) {
219225
$this->publicPort = null;
220226
}

0 commit comments

Comments
 (0)