Merge branch 'next' into fix-appwrite-template
This commit is contained in:
@@ -40,7 +40,7 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
$user = User::create([
|
$user = User::create([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => $input['name'],
|
'name' => $input['name'],
|
||||||
'email' => strtolower($input['email']),
|
'email' => $input['email'],
|
||||||
'password' => Hash::make($input['password']),
|
'password' => Hash::make($input['password']),
|
||||||
]);
|
]);
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
@@ -52,7 +52,7 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
} else {
|
} else {
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
'name' => $input['name'],
|
'name' => $input['name'],
|
||||||
'email' => strtolower($input['email']),
|
'email' => $input['email'],
|
||||||
'password' => Hash::make($input['password']),
|
'password' => Hash::make($input['password']),
|
||||||
]);
|
]);
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
|
use App\Jobs\DeleteResourceJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
@@ -72,7 +73,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
echo "Deleting stuck application: {$application->name}\n";
|
echo "Deleting stuck application: {$application->name}\n";
|
||||||
$application->forceDelete();
|
DeleteResourceJob::dispatch($application);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
@@ -82,26 +83,35 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($applicationsPreviews as $applicationPreview) {
|
foreach ($applicationsPreviews as $applicationPreview) {
|
||||||
if (! data_get($applicationPreview, 'application')) {
|
if (! data_get($applicationPreview, 'application')) {
|
||||||
echo "Deleting stuck application preview: {$applicationPreview->uuid}\n";
|
echo "Deleting stuck application preview: {$applicationPreview->uuid}\n";
|
||||||
$applicationPreview->delete();
|
DeleteResourceJob::dispatch($applicationPreview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
$applicationsPreviews = ApplicationPreview::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($applicationsPreviews as $applicationPreview) {
|
||||||
|
echo "Deleting stuck application preview: {$applicationPreview->fqdn}\n";
|
||||||
|
DeleteResourceJob::dispatch($applicationPreview);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
echo "Deleting stuck postgresql: {$postgresql->name}\n";
|
echo "Deleting stuck postgresql: {$postgresql->name}\n";
|
||||||
$postgresql->forceDelete();
|
DeleteResourceJob::dispatch($postgresql);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck postgresql: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck postgresql: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
|
$rediss = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($redis as $redis) {
|
foreach ($rediss as $redis) {
|
||||||
echo "Deleting stuck redis: {$redis->name}\n";
|
echo "Deleting stuck redis: {$redis->name}\n";
|
||||||
$redis->forceDelete();
|
DeleteResourceJob::dispatch($redis);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||||
@@ -110,7 +120,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
|
$keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($keydbs as $keydb) {
|
foreach ($keydbs as $keydb) {
|
||||||
echo "Deleting stuck keydb: {$keydb->name}\n";
|
echo "Deleting stuck keydb: {$keydb->name}\n";
|
||||||
$keydb->forceDelete();
|
DeleteResourceJob::dispatch($keydb);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
|
||||||
@@ -119,7 +129,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
|
$dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($dragonflies as $dragonfly) {
|
foreach ($dragonflies as $dragonfly) {
|
||||||
echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
|
echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
|
||||||
$dragonfly->forceDelete();
|
DeleteResourceJob::dispatch($dragonfly);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
|
||||||
@@ -128,7 +138,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
|
$clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($clickhouses as $clickhouse) {
|
foreach ($clickhouses as $clickhouse) {
|
||||||
echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
|
echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
|
||||||
$clickhouse->forceDelete();
|
DeleteResourceJob::dispatch($clickhouse);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
|
||||||
@@ -137,7 +147,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
echo "Deleting stuck mongodb: {$mongodb->name}\n";
|
echo "Deleting stuck mongodb: {$mongodb->name}\n";
|
||||||
$mongodb->forceDelete();
|
DeleteResourceJob::dispatch($mongodb);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck mongodb: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck mongodb: {$e->getMessage()}\n";
|
||||||
@@ -146,7 +156,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
|
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($mysqls as $mysql) {
|
foreach ($mysqls as $mysql) {
|
||||||
echo "Deleting stuck mysql: {$mysql->name}\n";
|
echo "Deleting stuck mysql: {$mysql->name}\n";
|
||||||
$mysql->forceDelete();
|
DeleteResourceJob::dispatch($mysql);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck mysql: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck mysql: {$e->getMessage()}\n";
|
||||||
@@ -155,7 +165,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
|
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($mariadbs as $mariadb) {
|
foreach ($mariadbs as $mariadb) {
|
||||||
echo "Deleting stuck mariadb: {$mariadb->name}\n";
|
echo "Deleting stuck mariadb: {$mariadb->name}\n";
|
||||||
$mariadb->forceDelete();
|
DeleteResourceJob::dispatch($mariadb);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck mariadb: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck mariadb: {$e->getMessage()}\n";
|
||||||
@@ -164,7 +174,7 @@ class CleanupStuckedResources extends Command
|
|||||||
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
|
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
echo "Deleting stuck service: {$service->name}\n";
|
echo "Deleting stuck service: {$service->name}\n";
|
||||||
$service->forceDelete();
|
DeleteResourceJob::dispatch($service);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck service: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck service: {$e->getMessage()}\n";
|
||||||
@@ -217,19 +227,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if (! data_get($application, 'environment')) {
|
if (! data_get($application, 'environment')) {
|
||||||
echo 'Application without environment: '.$application->name.'\n';
|
echo 'Application without environment: '.$application->name.'\n';
|
||||||
$application->forceDelete();
|
DeleteResourceJob::dispatch($application);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $application->destination()) {
|
if (! $application->destination()) {
|
||||||
echo 'Application without destination: '.$application->name.'\n';
|
echo 'Application without destination: '.$application->name.'\n';
|
||||||
$application->forceDelete();
|
DeleteResourceJob::dispatch($application);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($application, 'destination.server')) {
|
if (! data_get($application, 'destination.server')) {
|
||||||
echo 'Application without server: '.$application->name.'\n';
|
echo 'Application without server: '.$application->name.'\n';
|
||||||
$application->forceDelete();
|
DeleteResourceJob::dispatch($application);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -242,19 +252,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
if (! data_get($postgresql, 'environment')) {
|
if (! data_get($postgresql, 'environment')) {
|
||||||
echo 'Postgresql without environment: '.$postgresql->name.'\n';
|
echo 'Postgresql without environment: '.$postgresql->name.'\n';
|
||||||
$postgresql->forceDelete();
|
DeleteResourceJob::dispatch($postgresql);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $postgresql->destination()) {
|
if (! $postgresql->destination()) {
|
||||||
echo 'Postgresql without destination: '.$postgresql->name.'\n';
|
echo 'Postgresql without destination: '.$postgresql->name.'\n';
|
||||||
$postgresql->forceDelete();
|
DeleteResourceJob::dispatch($postgresql);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($postgresql, 'destination.server')) {
|
if (! data_get($postgresql, 'destination.server')) {
|
||||||
echo 'Postgresql without server: '.$postgresql->name.'\n';
|
echo 'Postgresql without server: '.$postgresql->name.'\n';
|
||||||
$postgresql->forceDelete();
|
DeleteResourceJob::dispatch($postgresql);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -267,19 +277,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($redis as $redis) {
|
foreach ($redis as $redis) {
|
||||||
if (! data_get($redis, 'environment')) {
|
if (! data_get($redis, 'environment')) {
|
||||||
echo 'Redis without environment: '.$redis->name.'\n';
|
echo 'Redis without environment: '.$redis->name.'\n';
|
||||||
$redis->forceDelete();
|
DeleteResourceJob::dispatch($redis);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $redis->destination()) {
|
if (! $redis->destination()) {
|
||||||
echo 'Redis without destination: '.$redis->name.'\n';
|
echo 'Redis without destination: '.$redis->name.'\n';
|
||||||
$redis->forceDelete();
|
DeleteResourceJob::dispatch($redis);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($redis, 'destination.server')) {
|
if (! data_get($redis, 'destination.server')) {
|
||||||
echo 'Redis without server: '.$redis->name.'\n';
|
echo 'Redis without server: '.$redis->name.'\n';
|
||||||
$redis->forceDelete();
|
DeleteResourceJob::dispatch($redis);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -293,19 +303,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
if (! data_get($mongodb, 'environment')) {
|
if (! data_get($mongodb, 'environment')) {
|
||||||
echo 'Mongodb without environment: '.$mongodb->name.'\n';
|
echo 'Mongodb without environment: '.$mongodb->name.'\n';
|
||||||
$mongodb->forceDelete();
|
DeleteResourceJob::dispatch($mongodb);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $mongodb->destination()) {
|
if (! $mongodb->destination()) {
|
||||||
echo 'Mongodb without destination: '.$mongodb->name.'\n';
|
echo 'Mongodb without destination: '.$mongodb->name.'\n';
|
||||||
$mongodb->forceDelete();
|
DeleteResourceJob::dispatch($mongodb);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($mongodb, 'destination.server')) {
|
if (! data_get($mongodb, 'destination.server')) {
|
||||||
echo 'Mongodb without server: '.$mongodb->name.'\n';
|
echo 'Mongodb without server: '.$mongodb->name.'\n';
|
||||||
$mongodb->forceDelete();
|
DeleteResourceJob::dispatch($mongodb);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -319,19 +329,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($mysqls as $mysql) {
|
foreach ($mysqls as $mysql) {
|
||||||
if (! data_get($mysql, 'environment')) {
|
if (! data_get($mysql, 'environment')) {
|
||||||
echo 'Mysql without environment: '.$mysql->name.'\n';
|
echo 'Mysql without environment: '.$mysql->name.'\n';
|
||||||
$mysql->forceDelete();
|
DeleteResourceJob::dispatch($mysql);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $mysql->destination()) {
|
if (! $mysql->destination()) {
|
||||||
echo 'Mysql without destination: '.$mysql->name.'\n';
|
echo 'Mysql without destination: '.$mysql->name.'\n';
|
||||||
$mysql->forceDelete();
|
DeleteResourceJob::dispatch($mysql);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($mysql, 'destination.server')) {
|
if (! data_get($mysql, 'destination.server')) {
|
||||||
echo 'Mysql without server: '.$mysql->name.'\n';
|
echo 'Mysql without server: '.$mysql->name.'\n';
|
||||||
$mysql->forceDelete();
|
DeleteResourceJob::dispatch($mysql);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -345,19 +355,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($mariadbs as $mariadb) {
|
foreach ($mariadbs as $mariadb) {
|
||||||
if (! data_get($mariadb, 'environment')) {
|
if (! data_get($mariadb, 'environment')) {
|
||||||
echo 'Mariadb without environment: '.$mariadb->name.'\n';
|
echo 'Mariadb without environment: '.$mariadb->name.'\n';
|
||||||
$mariadb->forceDelete();
|
DeleteResourceJob::dispatch($mariadb);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $mariadb->destination()) {
|
if (! $mariadb->destination()) {
|
||||||
echo 'Mariadb without destination: '.$mariadb->name.'\n';
|
echo 'Mariadb without destination: '.$mariadb->name.'\n';
|
||||||
$mariadb->forceDelete();
|
DeleteResourceJob::dispatch($mariadb);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($mariadb, 'destination.server')) {
|
if (! data_get($mariadb, 'destination.server')) {
|
||||||
echo 'Mariadb without server: '.$mariadb->name.'\n';
|
echo 'Mariadb without server: '.$mariadb->name.'\n';
|
||||||
$mariadb->forceDelete();
|
DeleteResourceJob::dispatch($mariadb);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -371,19 +381,19 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
if (! data_get($service, 'environment')) {
|
if (! data_get($service, 'environment')) {
|
||||||
echo 'Service without environment: '.$service->name.'\n';
|
echo 'Service without environment: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
DeleteResourceJob::dispatch($service);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $service->destination()) {
|
if (! $service->destination()) {
|
||||||
echo 'Service without destination: '.$service->name.'\n';
|
echo 'Service without destination: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
DeleteResourceJob::dispatch($service);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($service, 'server')) {
|
if (! data_get($service, 'server')) {
|
||||||
echo 'Service without server: '.$service->name.'\n';
|
echo 'Service without server: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
DeleteResourceJob::dispatch($service);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -396,7 +406,7 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($serviceApplications as $service) {
|
foreach ($serviceApplications as $service) {
|
||||||
if (! data_get($service, 'service')) {
|
if (! data_get($service, 'service')) {
|
||||||
echo 'ServiceApplication without service: '.$service->name.'\n';
|
echo 'ServiceApplication without service: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
DeleteResourceJob::dispatch($service);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -409,7 +419,7 @@ class CleanupStuckedResources extends Command
|
|||||||
foreach ($serviceDatabases as $service) {
|
foreach ($serviceDatabases as $service) {
|
||||||
if (! data_get($service, 'service')) {
|
if (! data_get($service, 'service')) {
|
||||||
echo 'ServiceDatabase without service: '.$service->name.'\n';
|
echo 'ServiceDatabase without service: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
DeleteResourceJob::dispatch($service);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ use App\Jobs\CheckHelperImageJob;
|
|||||||
use App\Jobs\PullChangelog;
|
use App\Jobs\PullChangelog;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
@@ -19,80 +20,18 @@ use Illuminate\Support\Facades\Http;
|
|||||||
|
|
||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'app:init {--force-cloud}';
|
protected $signature = 'app:init';
|
||||||
|
|
||||||
protected $description = 'Cleanup instance related stuffs';
|
protected $description = 'Cleanup instance related stuffs';
|
||||||
|
|
||||||
public $servers = null;
|
public $servers = null;
|
||||||
|
|
||||||
|
public InstanceSettings $settings;
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->optimize();
|
Artisan::call('optimize:clear');
|
||||||
|
Artisan::call('optimize');
|
||||||
if (isCloud() && ! $this->option('force-cloud')) {
|
|
||||||
echo "Skipping init as we are on cloud and --force-cloud option is not set\n";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->servers = Server::all();
|
|
||||||
if (! isCloud()) {
|
|
||||||
$this->sendAliveSignal();
|
|
||||||
get_public_ips();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backward compatibility
|
|
||||||
$this->replaceSlashInEnvironmentName();
|
|
||||||
$this->restoreCoolifyDbBackup();
|
|
||||||
$this->updateUserEmails();
|
|
||||||
//
|
|
||||||
$this->updateTraefikLabels();
|
|
||||||
if (! isCloud() || $this->option('force-cloud')) {
|
|
||||||
$this->cleanupUnusedNetworkFromCoolifyProxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->call('cleanup:redis');
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->call('cleanup:names');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in cleanup:names command: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
$this->call('cleanup:stucked-resources');
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->pullHelperImage();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCloud()) {
|
|
||||||
try {
|
|
||||||
$this->cleanupInProgressApplicationDeployments();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Could not cleanup inprogress deployments: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->pullTemplatesFromCDN();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Could not pull templates from CDN: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->pullChangelogFromGitHub();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Could not changelogs from github: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->cleanupInProgressApplicationDeployments();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Could not cleanup inprogress deployments: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->pullTemplatesFromCDN();
|
$this->pullTemplatesFromCDN();
|
||||||
@@ -105,20 +44,80 @@ class Init extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Could not changelogs from github: {$e->getMessage()}\n";
|
echo "Could not changelogs from github: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pullHelperImage();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in pullHelperImage command: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCloud()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->settings = instanceSettings();
|
||||||
|
$this->servers = Server::all();
|
||||||
|
|
||||||
|
$do_not_track = data_get($this->settings, 'do_not_track', true);
|
||||||
|
if ($do_not_track == false) {
|
||||||
|
$this->sendAliveSignal();
|
||||||
|
}
|
||||||
|
get_public_ips();
|
||||||
|
|
||||||
|
// Backward compatibility
|
||||||
|
$this->replaceSlashInEnvironmentName();
|
||||||
|
$this->restoreCoolifyDbBackup();
|
||||||
|
$this->updateUserEmails();
|
||||||
|
//
|
||||||
|
$this->updateTraefikLabels();
|
||||||
|
$this->cleanupUnusedNetworkFromCoolifyProxy();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->call('cleanup:redis');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleanup:redis command: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->call('cleanup:names');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleanup:names command: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->call('cleanup:stucked-resources');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleanup:stucked-resources command: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$updatedCount = ApplicationDeploymentQueue::whereIn('status', [
|
||||||
|
ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||||
|
ApplicationDeploymentStatus::QUEUED->value,
|
||||||
|
])->update([
|
||||||
|
'status' => ApplicationDeploymentStatus::FAILED->value,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($updatedCount > 0) {
|
||||||
|
echo "Marked {$updatedCount} stuck deployments as failed\n";
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Could not cleanup inprogress deployments: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$localhost = $this->servers->where('id', 0)->first();
|
$localhost = $this->servers->where('id', 0)->first();
|
||||||
$localhost->setupDynamicProxyConfiguration();
|
if ($localhost) {
|
||||||
|
$localhost->setupDynamicProxyConfiguration();
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
$settings = instanceSettings();
|
|
||||||
if (! is_null(config('constants.coolify.autoupdate', null))) {
|
if (! is_null(config('constants.coolify.autoupdate', null))) {
|
||||||
if (config('constants.coolify.autoupdate') == true) {
|
if (config('constants.coolify.autoupdate') == true) {
|
||||||
echo "Enabling auto-update\n";
|
echo "Enabling auto-update\n";
|
||||||
$settings->update(['is_auto_update_enabled' => true]);
|
$this->settings->update(['is_auto_update_enabled' => true]);
|
||||||
} else {
|
} else {
|
||||||
echo "Disabling auto-update\n";
|
echo "Disabling auto-update\n";
|
||||||
$settings->update(['is_auto_update_enabled' => false]);
|
$this->settings->update(['is_auto_update_enabled' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,17 +146,11 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function optimize()
|
|
||||||
{
|
|
||||||
Artisan::call('optimize:clear');
|
|
||||||
Artisan::call('optimize');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function updateUserEmails()
|
private function updateUserEmails()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
User::whereRaw('email ~ \'[A-Z]\'')->get()->each(function (User $user) {
|
User::whereRaw('email ~ \'[A-Z]\'')->get()->each(function (User $user) {
|
||||||
$user->update(['email' => strtolower($user->email)]);
|
$user->update(['email' => $user->email]);
|
||||||
});
|
});
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in updating user emails: {$e->getMessage()}\n";
|
echo "Error in updating user emails: {$e->getMessage()}\n";
|
||||||
@@ -173,27 +166,6 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanupUnnecessaryDynamicProxyConfiguration()
|
|
||||||
{
|
|
||||||
foreach ($this->servers as $server) {
|
|
||||||
try {
|
|
||||||
if (! $server->isFunctional()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($server->id === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$file = $server->proxyPath().'/dynamic/coolify.yaml';
|
|
||||||
|
|
||||||
return instant_remote_process([
|
|
||||||
"rm -f $file",
|
|
||||||
], $server, false);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in cleaning up unnecessary dynamic proxy configuration: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function cleanupUnusedNetworkFromCoolifyProxy()
|
private function cleanupUnusedNetworkFromCoolifyProxy()
|
||||||
{
|
{
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
@@ -263,13 +235,6 @@ class Init extends Command
|
|||||||
{
|
{
|
||||||
$id = config('app.id');
|
$id = config('app.id');
|
||||||
$version = config('constants.coolify.version');
|
$version = config('constants.coolify.version');
|
||||||
$settings = instanceSettings();
|
|
||||||
$do_not_track = data_get($settings, 'do_not_track');
|
|
||||||
if ($do_not_track == true) {
|
|
||||||
echo "Do_not_track is enabled\n";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
|
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -277,23 +242,6 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanupInProgressApplicationDeployments()
|
|
||||||
{
|
|
||||||
// Cleanup any failed deployments
|
|
||||||
try {
|
|
||||||
if (isCloud()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$queued_inprogress_deployments = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get();
|
|
||||||
foreach ($queued_inprogress_deployments as $deployment) {
|
|
||||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
|
||||||
$deployment->save();
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function replaceSlashInEnvironmentName()
|
private function replaceSlashInEnvironmentName()
|
||||||
{
|
{
|
||||||
if (version_compare('4.0.0-beta.298', config('constants.coolify.version'), '<=')) {
|
if (version_compare('4.0.0-beta.298', config('constants.coolify.version'), '<=')) {
|
||||||
|
@@ -1,98 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class InitChangelog extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'changelog:init {month? : Month in YYYY-MM format (defaults to current month)}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Initialize a new monthly changelog file with example structure';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$month = $this->argument('month') ?: Carbon::now()->format('Y-m');
|
|
||||||
|
|
||||||
// Validate month format
|
|
||||||
if (! preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $month)) {
|
|
||||||
$this->error('Invalid month format. Use YYYY-MM format with valid months 01-12 (e.g., 2025-08)');
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$changelogsDir = base_path('changelogs');
|
|
||||||
$filePath = $changelogsDir."/{$month}.json";
|
|
||||||
|
|
||||||
// Create changelogs directory if it doesn't exist
|
|
||||||
if (! is_dir($changelogsDir)) {
|
|
||||||
mkdir($changelogsDir, 0755, true);
|
|
||||||
$this->info("Created changelogs directory: {$changelogsDir}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if file already exists
|
|
||||||
if (file_exists($filePath)) {
|
|
||||||
if (! $this->confirm("File {$month}.json already exists. Overwrite?")) {
|
|
||||||
$this->info('Operation cancelled');
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the month for example data
|
|
||||||
$carbonMonth = Carbon::createFromFormat('Y-m', $month);
|
|
||||||
$monthName = $carbonMonth->format('F Y');
|
|
||||||
$sampleDate = $carbonMonth->addDays(14)->toISOString(); // Mid-month
|
|
||||||
|
|
||||||
// Get version from config
|
|
||||||
$version = 'v'.config('constants.coolify.version');
|
|
||||||
|
|
||||||
// Create example changelog structure
|
|
||||||
$exampleData = [
|
|
||||||
'entries' => [
|
|
||||||
[
|
|
||||||
'version' => $version,
|
|
||||||
'title' => 'Example Feature Release',
|
|
||||||
'content' => "This is an example changelog entry for {$monthName}. Replace this with your actual release notes. Include details about new features, improvements, bug fixes, and any breaking changes.",
|
|
||||||
'published_at' => $sampleDate,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
// Write the file
|
|
||||||
$jsonContent = json_encode($exampleData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
|
||||||
|
|
||||||
if (file_put_contents($filePath, $jsonContent) === false) {
|
|
||||||
$this->error("Failed to create changelog file: {$filePath}");
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info("✅ Created changelog file: changelogs/{$month}.json");
|
|
||||||
$this->line(" Example entry created for {$monthName}");
|
|
||||||
$this->line(' Edit the file to add your actual changelog entries');
|
|
||||||
|
|
||||||
// Show the file contents
|
|
||||||
if ($this->option('verbose')) {
|
|
||||||
$this->newLine();
|
|
||||||
$this->line('File contents:');
|
|
||||||
$this->line($jsonContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,7 +6,14 @@ use App\Jobs\DeleteResourceJob;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
|
use App\Models\StandaloneMariadb;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use function Laravel\Prompts\confirm;
|
use function Laravel\Prompts\confirm;
|
||||||
@@ -103,19 +110,79 @@ class ServicesDelete extends Command
|
|||||||
|
|
||||||
private function deleteDatabase()
|
private function deleteDatabase()
|
||||||
{
|
{
|
||||||
$databases = StandalonePostgresql::all();
|
// Collect all databases from all types with unique identifiers
|
||||||
if ($databases->count() === 0) {
|
$allDatabases = collect();
|
||||||
|
$databaseOptions = collect();
|
||||||
|
|
||||||
|
// Add PostgreSQL databases
|
||||||
|
foreach (StandalonePostgresql::all() as $db) {
|
||||||
|
$key = "postgresql_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (PostgreSQL)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add MySQL databases
|
||||||
|
foreach (StandaloneMysql::all() as $db) {
|
||||||
|
$key = "mysql_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (MySQL)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add MariaDB databases
|
||||||
|
foreach (StandaloneMariadb::all() as $db) {
|
||||||
|
$key = "mariadb_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (MariaDB)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add MongoDB databases
|
||||||
|
foreach (StandaloneMongodb::all() as $db) {
|
||||||
|
$key = "mongodb_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (MongoDB)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Redis databases
|
||||||
|
foreach (StandaloneRedis::all() as $db) {
|
||||||
|
$key = "redis_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (Redis)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add KeyDB databases
|
||||||
|
foreach (StandaloneKeydb::all() as $db) {
|
||||||
|
$key = "keydb_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (KeyDB)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Dragonfly databases
|
||||||
|
foreach (StandaloneDragonfly::all() as $db) {
|
||||||
|
$key = "dragonfly_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (Dragonfly)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ClickHouse databases
|
||||||
|
foreach (StandaloneClickhouse::all() as $db) {
|
||||||
|
$key = "clickhouse_{$db->id}";
|
||||||
|
$allDatabases->put($key, $db);
|
||||||
|
$databaseOptions->put($key, "{$db->name} (ClickHouse)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allDatabases->count() === 0) {
|
||||||
$this->error('There are no databases to delete.');
|
$this->error('There are no databases to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$databasesToDelete = multiselect(
|
$databasesToDelete = multiselect(
|
||||||
'What database do you want to delete?',
|
'What database do you want to delete?',
|
||||||
$databases->pluck('name', 'id')->sortKeys(),
|
$databaseOptions->sortKeys(),
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($databasesToDelete as $database) {
|
foreach ($databasesToDelete as $databaseKey) {
|
||||||
$toDelete = $databases->where('id', $database)->first();
|
$toDelete = $allDatabases->get($databaseKey);
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
||||||
|
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Webhook;
|
|||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Jobs\ApplicationPullRequestUpdateJob;
|
use App\Jobs\ApplicationPullRequestUpdateJob;
|
||||||
|
use App\Jobs\DeleteResourceJob;
|
||||||
use App\Jobs\GithubAppPermissionJob;
|
use App\Jobs\GithubAppPermissionJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
@@ -78,6 +79,7 @@ class Github extends Controller
|
|||||||
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
$branch = data_get($payload, 'pull_request.head.ref');
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
$base_branch = data_get($payload, 'pull_request.base.ref');
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
|
$author_association = data_get($payload, 'pull_request.author_association');
|
||||||
}
|
}
|
||||||
if (! $branch) {
|
if (! $branch) {
|
||||||
return response('Nothing to do. No branch found in the request.');
|
return response('Nothing to do. No branch found in the request.');
|
||||||
@@ -170,6 +172,19 @@ class Github extends Controller
|
|||||||
if ($x_github_event === 'pull_request') {
|
if ($x_github_event === 'pull_request') {
|
||||||
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
if ($application->isPRDeployable()) {
|
if ($application->isPRDeployable()) {
|
||||||
|
// Check if PR deployments from public contributors are restricted
|
||||||
|
if (! $application->settings->is_pr_deployments_public_enabled) {
|
||||||
|
$trustedAssociations = ['OWNER', 'MEMBER', 'COLLABORATOR', 'CONTRIBUTOR'];
|
||||||
|
if (! in_array($author_association, $trustedAssociations)) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'PR deployments are restricted to repository members and contributors. Author association: '.$author_association,
|
||||||
|
]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
@@ -226,9 +241,7 @@ class Github extends Controller
|
|||||||
if ($action === 'closed') {
|
if ($action === 'closed') {
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if ($found) {
|
if ($found) {
|
||||||
$found->delete();
|
DeleteResourceJob::dispatch($found);
|
||||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
@@ -327,6 +340,7 @@ class Github extends Controller
|
|||||||
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
$branch = data_get($payload, 'pull_request.head.ref');
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
$base_branch = data_get($payload, 'pull_request.base.ref');
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
|
$author_association = data_get($payload, 'pull_request.author_association');
|
||||||
}
|
}
|
||||||
if (! $id || ! $branch) {
|
if (! $id || ! $branch) {
|
||||||
return response('Nothing to do. No id or branch found.');
|
return response('Nothing to do. No id or branch found.');
|
||||||
@@ -400,6 +414,19 @@ class Github extends Controller
|
|||||||
if ($x_github_event === 'pull_request') {
|
if ($x_github_event === 'pull_request') {
|
||||||
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
if ($application->isPRDeployable()) {
|
if ($application->isPRDeployable()) {
|
||||||
|
// Check if PR deployments from public contributors are restricted
|
||||||
|
if (! $application->settings->is_pr_deployments_public_enabled) {
|
||||||
|
$trustedAssociations = ['OWNER', 'MEMBER', 'COLLABORATOR', 'CONTRIBUTOR'];
|
||||||
|
if (! in_array($author_association, $trustedAssociations)) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'PR deployments are restricted to repository members and contributors. Author association: '.$author_association,
|
||||||
|
]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
@@ -452,7 +479,8 @@ class Github extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
||||||
$found->delete();
|
|
||||||
|
DeleteResourceJob::dispatch($found);
|
||||||
|
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
@@ -78,6 +78,8 @@ class Index extends Component
|
|||||||
'new_email' => ['required', 'email', 'unique:users,email'],
|
'new_email' => ['required', 'email', 'unique:users,email'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->new_email = strtolower($this->new_email);
|
||||||
|
|
||||||
// Skip rate limiting in development mode
|
// Skip rate limiting in development mode
|
||||||
if (! isDev()) {
|
if (! isDev()) {
|
||||||
// Rate limit by current user's email (1 request per 2 minutes)
|
// Rate limit by current user's email (1 request per 2 minutes)
|
||||||
@@ -90,7 +92,7 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rate limit by new email address (3 requests per hour per email)
|
// Rate limit by new email address (3 requests per hour per email)
|
||||||
$newEmailKey = 'email-change:email:'.md5(strtolower($this->new_email));
|
$newEmailKey = 'email-change:email:'.md5($this->new_email);
|
||||||
if (! RateLimiter::attempt($newEmailKey, 3, function () {}, 3600)) {
|
if (! RateLimiter::attempt($newEmailKey, 3, function () {}, 3600)) {
|
||||||
$this->dispatch('error', 'This email address has received too many verification requests. Please try again later.');
|
$this->dispatch('error', 'This email address has received too many verification requests. Please try again later.');
|
||||||
|
|
||||||
|
@@ -28,6 +28,9 @@ class Advanced extends Component
|
|||||||
#[Validate(['boolean'])]
|
#[Validate(['boolean'])]
|
||||||
public bool $isPreviewDeploymentsEnabled = false;
|
public bool $isPreviewDeploymentsEnabled = false;
|
||||||
|
|
||||||
|
#[Validate(['boolean'])]
|
||||||
|
public bool $isPrDeploymentsPublicEnabled = false;
|
||||||
|
|
||||||
#[Validate(['boolean'])]
|
#[Validate(['boolean'])]
|
||||||
public bool $isAutoDeployEnabled = true;
|
public bool $isAutoDeployEnabled = true;
|
||||||
|
|
||||||
@@ -91,6 +94,7 @@ class Advanced extends Component
|
|||||||
$this->application->settings->is_git_lfs_enabled = $this->isGitLfsEnabled;
|
$this->application->settings->is_git_lfs_enabled = $this->isGitLfsEnabled;
|
||||||
$this->application->settings->is_git_shallow_clone_enabled = $this->isGitShallowCloneEnabled;
|
$this->application->settings->is_git_shallow_clone_enabled = $this->isGitShallowCloneEnabled;
|
||||||
$this->application->settings->is_preview_deployments_enabled = $this->isPreviewDeploymentsEnabled;
|
$this->application->settings->is_preview_deployments_enabled = $this->isPreviewDeploymentsEnabled;
|
||||||
|
$this->application->settings->is_pr_deployments_public_enabled = $this->isPrDeploymentsPublicEnabled;
|
||||||
$this->application->settings->is_auto_deploy_enabled = $this->isAutoDeployEnabled;
|
$this->application->settings->is_auto_deploy_enabled = $this->isAutoDeployEnabled;
|
||||||
$this->application->settings->is_log_drain_enabled = $this->isLogDrainEnabled;
|
$this->application->settings->is_log_drain_enabled = $this->isLogDrainEnabled;
|
||||||
$this->application->settings->is_gpu_enabled = $this->isGpuEnabled;
|
$this->application->settings->is_gpu_enabled = $this->isGpuEnabled;
|
||||||
@@ -117,6 +121,7 @@ class Advanced extends Component
|
|||||||
$this->isGitLfsEnabled = $this->application->settings->is_git_lfs_enabled;
|
$this->isGitLfsEnabled = $this->application->settings->is_git_lfs_enabled;
|
||||||
$this->isGitShallowCloneEnabled = $this->application->settings->is_git_shallow_clone_enabled ?? false;
|
$this->isGitShallowCloneEnabled = $this->application->settings->is_git_shallow_clone_enabled ?? false;
|
||||||
$this->isPreviewDeploymentsEnabled = $this->application->settings->is_preview_deployments_enabled;
|
$this->isPreviewDeploymentsEnabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
|
$this->isPrDeploymentsPublicEnabled = $this->application->settings->is_pr_deployments_public_enabled ?? false;
|
||||||
$this->isAutoDeployEnabled = $this->application->settings->is_auto_deploy_enabled;
|
$this->isAutoDeployEnabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
$this->isGpuEnabled = $this->application->settings->is_gpu_enabled;
|
$this->isGpuEnabled = $this->application->settings->is_gpu_enabled;
|
||||||
$this->gpuDriver = $this->application->settings->gpu_driver;
|
$this->gpuDriver = $this->application->settings->gpu_driver;
|
||||||
|
@@ -13,6 +13,7 @@ class ApplicationSetting extends Model
|
|||||||
'is_force_https_enabled' => 'boolean',
|
'is_force_https_enabled' => 'boolean',
|
||||||
'is_debug_enabled' => 'boolean',
|
'is_debug_enabled' => 'boolean',
|
||||||
'is_preview_deployments_enabled' => 'boolean',
|
'is_preview_deployments_enabled' => 'boolean',
|
||||||
|
'is_pr_deployments_public_enabled' => 'boolean',
|
||||||
'is_git_submodules_enabled' => 'boolean',
|
'is_git_submodules_enabled' => 'boolean',
|
||||||
'is_git_lfs_enabled' => 'boolean',
|
'is_git_lfs_enabled' => 'boolean',
|
||||||
'is_git_shallow_clone_enabled' => 'boolean',
|
'is_git_shallow_clone_enabled' => 'boolean',
|
||||||
|
@@ -56,6 +56,22 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
'email_change_code_expires_at' => 'datetime',
|
'email_change_code_expires_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the email attribute to lowercase.
|
||||||
|
*/
|
||||||
|
public function setEmailAttribute($value)
|
||||||
|
{
|
||||||
|
$this->attributes['email'] = strtolower($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the pending_email attribute to lowercase.
|
||||||
|
*/
|
||||||
|
public function setPendingEmailAttribute($value)
|
||||||
|
{
|
||||||
|
$this->attributes['pending_email'] = $value ? strtolower($value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
protected static function boot()
|
protected static function boot()
|
||||||
{
|
{
|
||||||
parent::boot();
|
parent::boot();
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
<?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('application_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_pr_deployments_public_enabled')->default(false)->after('is_preview_deployments_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_pr_deployments_public_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -13,6 +13,12 @@
|
|||||||
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
||||||
instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" canGate="update"
|
instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" canGate="update"
|
||||||
:canResource="$application" />
|
:canResource="$application" />
|
||||||
|
@if ($isPreviewDeploymentsEnabled)
|
||||||
|
<x-forms.checkbox
|
||||||
|
helper="When enabled, anyone can trigger PR deployments. When disabled, only repository members, collaborators, and contributors can trigger PR deployments."
|
||||||
|
instantSave id="isPrDeploymentsPublicEnabled" label="Allow Public PR Deployments" canGate="update"
|
||||||
|
:canResource="$application" />
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
<x-forms.checkbox helper="Disable Docker build cache on every deployment." instantSave
|
<x-forms.checkbox helper="Disable Docker build cache on every deployment." instantSave
|
||||||
id="disableBuildCache" label="Disable Build Cache" canGate="update" :canResource="$application" />
|
id="disableBuildCache" label="Disable Build Cache" canGate="update" :canResource="$application" />
|
||||||
|
Reference in New Issue
Block a user