Able to backup Coolify itself
This commit is contained in:
		
							
								
								
									
										80
									
								
								.github/workflows/coolify-builder.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								.github/workflows/coolify-builder.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,80 +0,0 @@
 | 
			
		||||
name: Coolify Builder (v4)
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: ["main", "next"]
 | 
			
		||||
    paths:
 | 
			
		||||
      - .github/workflows/coolify-builder.yml
 | 
			
		||||
      - docker/coolify-builder/Dockerfile
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify-builder"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  amd64:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-builder/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [self-hosted, arm64]
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-builder/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:aarch64
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [amd64, aarch64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
@@ -6,6 +6,7 @@ use App\Models\InstanceSettings;
 | 
			
		||||
use App\Models\Project;
 | 
			
		||||
use App\Models\S3Storage;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\StandalonePostgresql;
 | 
			
		||||
use App\Models\TeamInvitation;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
 | 
			
		||||
@@ -60,20 +61,16 @@ class Controller extends BaseController
 | 
			
		||||
    {
 | 
			
		||||
        if (auth()->user()->isInstanceAdmin()) {
 | 
			
		||||
            $settings = InstanceSettings::get();
 | 
			
		||||
            $database = StandalonePostgresql::whereName('coolify-db')->first();
 | 
			
		||||
            if ($database) {
 | 
			
		||||
                $backup = $database->scheduledBackups->first();
 | 
			
		||||
                $s3s = S3Storage::whereTeamId(0)->get();
 | 
			
		||||
            }
 | 
			
		||||
            return view('settings.configuration', [
 | 
			
		||||
                'settings' => $settings
 | 
			
		||||
            ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function emails()
 | 
			
		||||
    {
 | 
			
		||||
        if (auth()->user()->isInstanceAdmin()) {
 | 
			
		||||
            $settings = InstanceSettings::get();
 | 
			
		||||
            return view('settings.emails', [
 | 
			
		||||
                'settings' => $settings
 | 
			
		||||
                'settings' => $settings,
 | 
			
		||||
                'database' => $database,
 | 
			
		||||
                'backup' => $backup ?? null,
 | 
			
		||||
                's3s' => $s3s ?? [],
 | 
			
		||||
            ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,12 @@ class DatabaseController extends Controller
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
        $executions = collect($backup->executions)->sortByDesc('created_at');
 | 
			
		||||
        return view('project.database.backups.executions', ['database' => $database, 'backup' => $backup, 'executions' => $executions]);
 | 
			
		||||
        return view('project.database.backups.executions', [
 | 
			
		||||
            'database' => $database,
 | 
			
		||||
            'backup' => $backup,
 | 
			
		||||
            'executions' => $executions,
 | 
			
		||||
            's3s' => auth()->user()->currentTeam()->s3s,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function backups()
 | 
			
		||||
@@ -63,6 +68,9 @@ class DatabaseController extends Controller
 | 
			
		||||
        if (!$database) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
        return view('project.database.backups.all', ['database' => $database]);
 | 
			
		||||
        return view('project.database.backups.all', [
 | 
			
		||||
            'database' => $database,
 | 
			
		||||
            's3s' => auth()->user()->currentTeam()->s3s,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ use Livewire\Component;
 | 
			
		||||
class BackupEdit extends Component
 | 
			
		||||
{
 | 
			
		||||
    public $backup;
 | 
			
		||||
    public $s3s;
 | 
			
		||||
    public array $parameters;
 | 
			
		||||
 | 
			
		||||
    protected $rules = [
 | 
			
		||||
@@ -14,17 +15,25 @@ class BackupEdit extends Component
 | 
			
		||||
        'backup.frequency' => 'required|string',
 | 
			
		||||
        'backup.number_of_backups_locally' => 'required|integer|min:1',
 | 
			
		||||
        'backup.save_s3' => 'required|boolean',
 | 
			
		||||
        'backup.s3_storage_id' => 'nullable|integer',
 | 
			
		||||
    ];
 | 
			
		||||
    protected $validationAttributes = [
 | 
			
		||||
        'backup.enabled' => 'Enabled',
 | 
			
		||||
        'backup.frequency' => 'Frequency',
 | 
			
		||||
        'backup.number_of_backups_locally' => 'Number of Backups Locally',
 | 
			
		||||
        'backup.save_s3' => 'Save to S3',
 | 
			
		||||
        'backup.s3_storage_id' => 'S3 Storage',
 | 
			
		||||
    ];
 | 
			
		||||
    protected $messages = [
 | 
			
		||||
        'backup.s3_storage_id' => 'Select a S3 Storage',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        $this->parameters = get_route_parameters();
 | 
			
		||||
        if (is_null($this->backup->s3_storage_id)) {
 | 
			
		||||
            $this->backup->s3_storage_id = 'default';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -37,21 +46,43 @@ class BackupEdit extends Component
 | 
			
		||||
 | 
			
		||||
    public function instantSave()
 | 
			
		||||
    {
 | 
			
		||||
        $this->backup->save();
 | 
			
		||||
        $this->backup->refresh();
 | 
			
		||||
        $this->emit('success', 'Backup updated successfully');
 | 
			
		||||
        try {
 | 
			
		||||
            $this->custom_validate();
 | 
			
		||||
            $this->backup->save();
 | 
			
		||||
            $this->backup->refresh();
 | 
			
		||||
            $this->emit('success', 'Backup updated successfully');
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            $this->emit('error', $e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function custom_validate()
 | 
			
		||||
    {
 | 
			
		||||
//        if ($this->backup->save_s3) {
 | 
			
		||||
//            if (!is_numeric($this->selected_storage_id)) {
 | 
			
		||||
//                throw new \Exception('Invalid S3 Storage');
 | 
			
		||||
//            } else {
 | 
			
		||||
//                $this->backup->s3_storage_id = $this->selected_storage_id;
 | 
			
		||||
//            }
 | 
			
		||||
//        }
 | 
			
		||||
        $isValid = validate_cron_expression($this->backup->frequency);
 | 
			
		||||
        if (!$isValid) {
 | 
			
		||||
            throw new \Exception('Invalid Cron / Human expression');
 | 
			
		||||
        }
 | 
			
		||||
        $this->validate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function submit()
 | 
			
		||||
    {
 | 
			
		||||
        $isValid = validate_cron_expression($this->backup->frequency);
 | 
			
		||||
        if (!$isValid) {
 | 
			
		||||
            $this->emit('error', 'Invalid Cron / Human expression');
 | 
			
		||||
            return;
 | 
			
		||||
        ray($this->backup->s3_storage_id);
 | 
			
		||||
        try {
 | 
			
		||||
            $this->custom_validate();
 | 
			
		||||
            $this->backup->save();
 | 
			
		||||
            $this->backup->refresh();
 | 
			
		||||
            $this->emit('success', 'Backup updated successfully');
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            $this->emit('error', $e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        $this->validate();
 | 
			
		||||
        $this->backup->save();
 | 
			
		||||
        $this->backup->refresh();
 | 
			
		||||
        $this->emit('success', 'Backup updated successfully');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@ class CreateScheduledBackup extends Component
 | 
			
		||||
    public $frequency;
 | 
			
		||||
    public bool $enabled = true;
 | 
			
		||||
    public bool $save_s3 = true;
 | 
			
		||||
    public $s3_storage_id;
 | 
			
		||||
    public $s3s;
 | 
			
		||||
 | 
			
		||||
    protected $rules = [
 | 
			
		||||
        'frequency' => 'required|string',
 | 
			
		||||
@@ -27,13 +29,14 @@ class CreateScheduledBackup extends Component
 | 
			
		||||
            $this->validate();
 | 
			
		||||
            $isValid = validate_cron_expression($this->frequency);
 | 
			
		||||
            if (!$isValid) {
 | 
			
		||||
                $this->emit('error', 'Invalid Cron / Human expression');
 | 
			
		||||
                $this->emit('error', 'Invalid Cron / Human expression.');
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            ScheduledDatabaseBackup::create([
 | 
			
		||||
                'enabled' => true,
 | 
			
		||||
                'frequency' => $this->frequency,
 | 
			
		||||
                'save_s3' => $this->save_s3,
 | 
			
		||||
                's3_storage_id' => $this->s3_storage_id,
 | 
			
		||||
                'database_id' => $this->database->id,
 | 
			
		||||
                'database_type' => $this->database->getMorphClass(),
 | 
			
		||||
                'team_id' => auth()->user()->currentTeam()->id,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										75
									
								
								app/Http/Livewire/Settings/Backup.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								app/Http/Livewire/Settings/Backup.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Livewire\Settings;
 | 
			
		||||
 | 
			
		||||
use App\Models\InstanceSettings;
 | 
			
		||||
use App\Models\S3Storage;
 | 
			
		||||
use App\Models\ScheduledDatabaseBackup;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\StandalonePostgresql;
 | 
			
		||||
use Livewire\Component;
 | 
			
		||||
 | 
			
		||||
class Backup extends Component
 | 
			
		||||
{
 | 
			
		||||
    public InstanceSettings $settings;
 | 
			
		||||
    public $s3s;
 | 
			
		||||
    public StandalonePostgresql|null $database = null;
 | 
			
		||||
    public ScheduledDatabaseBackup|null $backup = null;
 | 
			
		||||
 | 
			
		||||
    protected $rules = [
 | 
			
		||||
        'database.uuid' => 'required',
 | 
			
		||||
        'database.name' => 'required',
 | 
			
		||||
        'database.description' => 'nullable',
 | 
			
		||||
        'database.postgres_user' => 'required',
 | 
			
		||||
        'database.postgres_password' => 'required',
 | 
			
		||||
 | 
			
		||||
    ];
 | 
			
		||||
    protected $validationAttributes = [
 | 
			
		||||
        'database.uuid' => 'uuid',
 | 
			
		||||
        'database.name' => 'name',
 | 
			
		||||
        'database.description' => 'description',
 | 
			
		||||
        'database.postgres_user' => 'postgres user',
 | 
			
		||||
        'database.postgres_password' => 'postgres password',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function add_coolify_database()
 | 
			
		||||
    {
 | 
			
		||||
        ray('add_coolify_database');
 | 
			
		||||
        $server = Server::find(0);
 | 
			
		||||
        $out = instant_remote_process(['docker inspect coolify-db'], $server);
 | 
			
		||||
        $envs = format_docker_envs_to_json($out);
 | 
			
		||||
        $postgres_password = $envs['POSTGRES_PASSWORD'];
 | 
			
		||||
        $postgres_user = $envs['POSTGRES_USER'];
 | 
			
		||||
        $postgres_db = $envs['POSTGRES_DB'];
 | 
			
		||||
        $this->database = StandalonePostgresql::create([
 | 
			
		||||
            'id' => 0,
 | 
			
		||||
            'name' => 'coolify-db',
 | 
			
		||||
            'description' => 'Coolify database',
 | 
			
		||||
            'postgres_user' => $postgres_user,
 | 
			
		||||
            'postgres_password' => $postgres_password,
 | 
			
		||||
            'postgres_db' => $postgres_db,
 | 
			
		||||
            'status' => 'running',
 | 
			
		||||
            'destination_type' => 'App\Models\StandaloneDocker',
 | 
			
		||||
            'destination_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
        $this->backup = ScheduledDatabaseBackup::create([
 | 
			
		||||
            'id' => 0,
 | 
			
		||||
            'enabled' => true,
 | 
			
		||||
            'save_s3' => false,
 | 
			
		||||
            'frequency' => '0 0 * * *',
 | 
			
		||||
            'database_id' => $this->database->id,
 | 
			
		||||
            'database_type' => 'App\Models\StandalonePostgresql',
 | 
			
		||||
            'team_id' => auth()->user()->currentTeam()->id,
 | 
			
		||||
        ]);
 | 
			
		||||
        $this->database->refresh();
 | 
			
		||||
        $this->backup->refresh();
 | 
			
		||||
        ray($this->backup);
 | 
			
		||||
        $this->s3s = S3Storage::whereTeamId(0)->get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function submit()
 | 
			
		||||
    {
 | 
			
		||||
        $this->emit('success', 'Backup updated successfully');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -176,10 +176,10 @@ class ApplicationDeploymentJob implements ShouldQueue
 | 
			
		||||
    {
 | 
			
		||||
        $this->execute_remote_command(
 | 
			
		||||
            [
 | 
			
		||||
                "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder).'",
 | 
			
		||||
                "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-helper).'",
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder",
 | 
			
		||||
                "docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-helper",
 | 
			
		||||
                "hidden" => true,
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
 
 | 
			
		||||
@@ -30,22 +30,25 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
    public StandalonePostgresql $database;
 | 
			
		||||
    public string $database_status;
 | 
			
		||||
 | 
			
		||||
    public string|null $container_name = null;
 | 
			
		||||
    public ScheduledDatabaseBackupExecution|null $backup_log = null;
 | 
			
		||||
    public string $backup_status;
 | 
			
		||||
    public string|null $backup_filename = null;
 | 
			
		||||
    public string|null $backup_location = null;
 | 
			
		||||
    public string $backup_dir;
 | 
			
		||||
    public string $backup_file;
 | 
			
		||||
    public int $size = 0;
 | 
			
		||||
    public string|null $backup_output = null;
 | 
			
		||||
    public S3Storage $s3;
 | 
			
		||||
    public S3Storage|null $s3 = null;
 | 
			
		||||
 | 
			
		||||
    public function __construct($backup)
 | 
			
		||||
    {
 | 
			
		||||
        $this->backup = $backup;
 | 
			
		||||
        $this->team = Team::find($backup->team_id);
 | 
			
		||||
        $this->database = $this->backup->database->first();
 | 
			
		||||
        $this->database = $this->backup->database;
 | 
			
		||||
        $this->database_type = $this->database->type();
 | 
			
		||||
        $this->server = $this->database->destination->server;
 | 
			
		||||
        $this->database_status = $this->database->status;
 | 
			
		||||
        $this->s3 = $this->team->s3;
 | 
			
		||||
        $this->s3 = $this->backup->s3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function middleware(): array
 | 
			
		||||
@@ -58,16 +61,23 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
        return $this->backup->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->database_status !== 'running') {
 | 
			
		||||
            ray('database not running');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $this->backup_filename = backup_dir() . "/{$this->database->uuid}/dumpall-" . Carbon::now()->timestamp . ".sql";
 | 
			
		||||
        $this->container_name = $this->database->uuid;
 | 
			
		||||
        if ($this->database->name === 'coolify-db') {
 | 
			
		||||
            $this->container_name = "coolify-db";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->backup_dir = backup_dir() . "/" . $this->container_name;
 | 
			
		||||
        $this->backup_file = "/dumpall-" . Carbon::now()->timestamp . ".sql";
 | 
			
		||||
        $this->backup_location = $this->backup_dir . $this->backup_file;
 | 
			
		||||
 | 
			
		||||
        $this->backup_log = ScheduledDatabaseBackupExecution::create([
 | 
			
		||||
            'filename' => $this->backup_filename,
 | 
			
		||||
            'filename' => $this->backup_location,
 | 
			
		||||
            'scheduled_database_backup_id' => $this->backup->id,
 | 
			
		||||
        ]);
 | 
			
		||||
        if ($this->database_type === 'standalone-postgresql') {
 | 
			
		||||
@@ -76,18 +86,17 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
        $this->calculate_size();
 | 
			
		||||
        $this->remove_old_backups();
 | 
			
		||||
        if ($this->backup->save_s3) {
 | 
			
		||||
//            $this->upload_to_s3();
 | 
			
		||||
            $this->upload_to_s3();
 | 
			
		||||
        }
 | 
			
		||||
        $this->save_backup_logs();
 | 
			
		||||
        // TODO: Notify user
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function backup_standalone_postgresql()
 | 
			
		||||
    private function backup_standalone_postgresql(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $commands[] = "mkdir -p " . backup_dir();
 | 
			
		||||
            $commands[] = "mkdir -p " . backup_dir() . "/{$this->database->uuid}";
 | 
			
		||||
            $commands[] = "docker exec {$this->database->uuid} pg_dumpall -U {$this->database->postgres_user} > $this->backup_filename";
 | 
			
		||||
            $commands[] = "mkdir -p " . $this->backup_dir;
 | 
			
		||||
            $commands[] = "docker exec $this->container_name pg_dumpall -U {$this->database->postgres_user} > $this->backup_location";
 | 
			
		||||
 | 
			
		||||
            $this->backup_output = instant_remote_process($commands, $this->server);
 | 
			
		||||
 | 
			
		||||
@@ -97,14 +106,14 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
                $this->backup_output = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ray('Backup done for ' . $this->database->uuid . ' at ' . $this->server->name . ':' . $this->backup_filename);
 | 
			
		||||
            ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
 | 
			
		||||
 | 
			
		||||
            $this->backup_status = 'success';
 | 
			
		||||
            $this->team->notify(new BackupSuccess($this->backup, $this->database));
 | 
			
		||||
        } catch (Throwable $th) {
 | 
			
		||||
            $this->backup_status = 'failed';
 | 
			
		||||
            $this->add_to_backup_output($th->getMessage());
 | 
			
		||||
            ray('Backup failed for ' . $this->database->uuid . ' at ' . $this->server->name . ':' . $this->backup_filename . '\n\nError:' . $th->getMessage());
 | 
			
		||||
            ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $th->getMessage());
 | 
			
		||||
            $this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
 | 
			
		||||
        } finally {
 | 
			
		||||
            $this->backup_log->update([
 | 
			
		||||
@@ -113,7 +122,7 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function add_to_backup_output($output)
 | 
			
		||||
    private function add_to_backup_output($output): void
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->backup_output) {
 | 
			
		||||
            $this->backup_output = $this->backup_output . "\n" . $output;
 | 
			
		||||
@@ -122,12 +131,12 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function calculate_size()
 | 
			
		||||
    private function calculate_size(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->size = instant_remote_process(["du -b $this->backup_filename | cut -f1"], $this->server);
 | 
			
		||||
        $this->size = instant_remote_process(["du -b $this->backup_location | cut -f1"], $this->server);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function remove_old_backups()
 | 
			
		||||
    private function remove_old_backups(): void
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->backup->number_of_backups_locally === 0) {
 | 
			
		||||
            $deletable = $this->backup->executions()->where('status', 'success');
 | 
			
		||||
@@ -140,17 +149,7 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function save_backup_logs()
 | 
			
		||||
    {
 | 
			
		||||
        $this->backup_log->update([
 | 
			
		||||
            'status' => $this->backup_status,
 | 
			
		||||
            'message' => $this->backup_output,
 | 
			
		||||
            'size' => $this->size,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function upload_to_s3()
 | 
			
		||||
    private function upload_to_s3(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (is_null($this->s3)) {
 | 
			
		||||
@@ -161,16 +160,29 @@ class DatabaseBackupJob implements ShouldQueue
 | 
			
		||||
//            $region = $this->s3->region;
 | 
			
		||||
            $bucket = $this->s3->bucket;
 | 
			
		||||
            $endpoint = $this->s3->endpoint;
 | 
			
		||||
            $backup_dir = backup_dir() . "/{$this->database->uuid}";
 | 
			
		||||
 | 
			
		||||
            $base_command = "docker run -t --network {$this->database->destination->network} -v {$this->backup_filename}:{$this->backup_filename}:ro --rm --entrypoint=/bin/sh minio/mc -c \"mc config host add temporary {$endpoint} $key $secret && mc cp $this->backup_filename temporary/$bucket/$backup_dir/ \"";
 | 
			
		||||
 | 
			
		||||
            instant_remote_process([$base_command], $this->server);
 | 
			
		||||
 | 
			
		||||
            $commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
 | 
			
		||||
            $commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
 | 
			
		||||
            $commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
 | 
			
		||||
            instant_remote_process($commands, $this->server);
 | 
			
		||||
            $this->add_to_backup_output('Uploaded to S3.');
 | 
			
		||||
            ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir);
 | 
			
		||||
        } catch (\Throwable $th) {
 | 
			
		||||
            $this->add_to_backup_output($th->getMessage());
 | 
			
		||||
            ray($th->getMessage());
 | 
			
		||||
        } finally {
 | 
			
		||||
            $command = "docker rm -f backup-of-{$this->backup->uuid}";
 | 
			
		||||
            instant_remote_process([$command], $this->server);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function save_backup_logs(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->backup_log->update([
 | 
			
		||||
            'status' => $this->backup_status,
 | 
			
		||||
            'message' => $this->backup_output,
 | 
			
		||||
            'size' => $this->size,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,25 +8,7 @@ use Spatie\Activitylog\Models\Activity;
 | 
			
		||||
 | 
			
		||||
class Application extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'name',
 | 
			
		||||
        'repository_project_id',
 | 
			
		||||
        'project_id',
 | 
			
		||||
        'description',
 | 
			
		||||
        'git_repository',
 | 
			
		||||
        'git_branch',
 | 
			
		||||
        'git_full_url',
 | 
			
		||||
        'build_pack',
 | 
			
		||||
        'environment_id',
 | 
			
		||||
        'destination_id',
 | 
			
		||||
        'destination_type',
 | 
			
		||||
        'source_id',
 | 
			
		||||
        'source_type',
 | 
			
		||||
        'ports_mappings',
 | 
			
		||||
        'ports_exposes',
 | 
			
		||||
        'publish_directory',
 | 
			
		||||
        'private_key_id'
 | 
			
		||||
    ];
 | 
			
		||||
    protected $guarded = [];
 | 
			
		||||
 | 
			
		||||
    protected static function booted()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
class Database extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    public function environment()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsTo(Environment::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function destination()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->morphTo();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,4 +25,9 @@ class ScheduledDatabaseBackup extends BaseModel
 | 
			
		||||
    {
 | 
			
		||||
        return $this->hasMany(ScheduledDatabaseBackupExecution::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function s3()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsTo(S3Storage::class, 's3_storage_id');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Service extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    public function environment()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsTo(Environment::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function destination()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->morphTo();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,11 +4,7 @@ namespace App\Models;
 | 
			
		||||
 | 
			
		||||
class StandaloneDocker extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'name',
 | 
			
		||||
        'network',
 | 
			
		||||
        'server_id',
 | 
			
		||||
    ];
 | 
			
		||||
    protected $guarded = [];
 | 
			
		||||
 | 
			
		||||
    public function applications()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -84,8 +84,8 @@ class Team extends Model implements SendsDiscord, SendsEmail
 | 
			
		||||
        return $sources;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function s3()
 | 
			
		||||
    public function s3s()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->hasOne(S3Storage::class);
 | 
			
		||||
        return $this->hasMany(S3Storage::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,6 @@ class BackupSuccess extends Notification implements ShouldQueue
 | 
			
		||||
        if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
 | 
			
		||||
            $channels[] = DiscordChannel::class;
 | 
			
		||||
        }
 | 
			
		||||
        ray($channels);
 | 
			
		||||
        return $channels;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,19 @@ function format_docker_labels_to_json($rawOutput): Collection
 | 
			
		||||
        })[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function format_docker_envs_to_json($rawOutput)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        $outputLines = json_decode($rawOutput, true, flags: JSON_THROW_ON_ERROR);
 | 
			
		||||
        return collect(data_get($outputLines[0], 'Config.Env', []))->mapWithKeys(function ($env) {
 | 
			
		||||
            $env = explode('=', $env);
 | 
			
		||||
            return [$env[0] => $env[1]];
 | 
			
		||||
        });
 | 
			
		||||
    } catch (\Throwable $th) {
 | 
			
		||||
        return collect([]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_container_status(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
 | 
			
		||||
{
 | 
			
		||||
    $container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ return new class extends Migration {
 | 
			
		||||
            $table->timestamp('started_at')->nullable();
 | 
			
		||||
            $table->morphs('destination');
 | 
			
		||||
 | 
			
		||||
            $table->foreignId('environment_id');
 | 
			
		||||
            $table->foreignId('environment_id')->nullable();
 | 
			
		||||
            $table->timestamps();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,14 @@ return new class extends Migration {
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('scheduled_database_backups', function (Blueprint $table) {
 | 
			
		||||
            $table->id();
 | 
			
		||||
            $table->text('description')->nullable();
 | 
			
		||||
            $table->string('uuid')->unique();
 | 
			
		||||
            $table->boolean('enabled')->default(true);
 | 
			
		||||
            $table->string('save_s3')->default(true);
 | 
			
		||||
            $table->boolean('save_s3')->default(true);
 | 
			
		||||
            $table->string('frequency');
 | 
			
		||||
            $table->integer('number_of_backups_locally')->default(7);
 | 
			
		||||
            $table->morphs('database');
 | 
			
		||||
            $table->foreignId('s3_storage_id')->nullable();
 | 
			
		||||
            $table->foreignId('team_id');
 | 
			
		||||
            $table->timestamps();
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Data\ApplicationPreview;
 | 
			
		||||
use App\Models\Application;
 | 
			
		||||
use App\Models\GithubApp;
 | 
			
		||||
use App\Models\StandaloneDocker;
 | 
			
		||||
@@ -15,8 +14,6 @@ class ApplicationSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $github_public_source = GithubApp::where('name', 'Public GitHub')->first();
 | 
			
		||||
 | 
			
		||||
        Application::create([
 | 
			
		||||
            'name' => 'coollabsio/coolify-examples:nodejs-fastify',
 | 
			
		||||
            'description' => 'NodeJS Fastify Example',
 | 
			
		||||
@@ -28,9 +25,9 @@ class ApplicationSeeder extends Seeder
 | 
			
		||||
            'ports_exposes' => '3000',
 | 
			
		||||
            'ports_mappings' => '3000:3000',
 | 
			
		||||
            'environment_id' => 1,
 | 
			
		||||
            'destination_id' => 1,
 | 
			
		||||
            'destination_id' => 0,
 | 
			
		||||
            'destination_type' => StandaloneDocker::class,
 | 
			
		||||
            'source_id' => $github_public_source->id,
 | 
			
		||||
            'source_id' => 1,
 | 
			
		||||
            'source_type' => GithubApp::class
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Environment;
 | 
			
		||||
use App\Models\StandaloneDocker;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class DBSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $environment_1 = Environment::find(1);
 | 
			
		||||
        $standalone_docker_1 = StandaloneDocker::find(1);
 | 
			
		||||
        // Database::create([
 | 
			
		||||
        //     'id' => 1,
 | 
			
		||||
        //     'name'=> "My first database",
 | 
			
		||||
        //     'environment_id' => $environment_1->id,
 | 
			
		||||
        //     'destination_id' => $standalone_docker_1->id,
 | 
			
		||||
        //     'destination_type' => StandaloneDocker::class,
 | 
			
		||||
        // ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -26,8 +26,6 @@ class DatabaseSeeder extends Seeder
 | 
			
		||||
            ApplicationSeeder::class,
 | 
			
		||||
            ApplicationSettingsSeeder::class,
 | 
			
		||||
            ApplicationPreviewSeeder::class,
 | 
			
		||||
            DBSeeder::class,
 | 
			
		||||
            ServiceSeeder::class,
 | 
			
		||||
            EnvironmentVariableSeeder::class,
 | 
			
		||||
            LocalPersistentVolumeSeeder::class,
 | 
			
		||||
            S3StorageSeeder::class,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\EnvironmentVariable;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class EnvironmentVariableSeeder extends Seeder
 | 
			
		||||
@@ -12,11 +11,11 @@ class EnvironmentVariableSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        EnvironmentVariable::create([
 | 
			
		||||
            'key' => 'NODE_ENV',
 | 
			
		||||
            'value' => 'production',
 | 
			
		||||
            'is_build_time' => true,
 | 
			
		||||
            'application_id' => 1,
 | 
			
		||||
        ]);
 | 
			
		||||
//        EnvironmentVariable::create([
 | 
			
		||||
//            'key' => 'NODE_ENV',
 | 
			
		||||
//            'value' => 'production',
 | 
			
		||||
//            'is_build_time' => true,
 | 
			
		||||
//            'application_id' => 1,
 | 
			
		||||
//        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\GithubApp;
 | 
			
		||||
use App\Models\PrivateKey;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class GithubAppSeeder extends Seeder
 | 
			
		||||
@@ -14,14 +12,12 @@ class GithubAppSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $root_team = Team::find(0);
 | 
			
		||||
        $private_key_2 = PrivateKey::find(1);
 | 
			
		||||
        GithubApp::create([
 | 
			
		||||
            'name' => 'Public GitHub',
 | 
			
		||||
            'api_url' => 'https://api.github.com',
 | 
			
		||||
            'html_url' => 'https://github.com',
 | 
			
		||||
            'is_public' => true,
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
        GithubApp::create([
 | 
			
		||||
            'name' => 'coolify-laravel-development-public',
 | 
			
		||||
@@ -34,8 +30,8 @@ class GithubAppSeeder extends Seeder
 | 
			
		||||
            'client_id' => 'Iv1.220e564d2b0abd8c',
 | 
			
		||||
            'client_secret' => '116d1d80289f378410dd70ab4e4b81dd8d2c52b6',
 | 
			
		||||
            'webhook_secret' => '326a47b49054f03288f800d81247ec9414d0abf3',
 | 
			
		||||
            'private_key_id' => $private_key_2->id,
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'private_key_id' => 1,
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\GitlabApp;
 | 
			
		||||
use App\Models\PrivateKey;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class GitlabAppSeeder extends Seeder
 | 
			
		||||
@@ -14,15 +12,13 @@ class GitlabAppSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $root_team = Team::find(0);
 | 
			
		||||
        $private_key_2 = PrivateKey::find(2);
 | 
			
		||||
        GitlabApp::create([
 | 
			
		||||
            'id' => 1,
 | 
			
		||||
            'name' => 'Public GitLab',
 | 
			
		||||
            'api_url' => 'https://gitlab.com/api/v4',
 | 
			
		||||
            'html_url' => 'https://gitlab.com',
 | 
			
		||||
            'is_public' => true,
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
        GitlabApp::create([
 | 
			
		||||
            'id' => 2,
 | 
			
		||||
@@ -35,8 +31,8 @@ class GitlabAppSeeder extends Seeder
 | 
			
		||||
            'deploy_key_id' => '1234',
 | 
			
		||||
            'public_key' => 'dfjasiourj',
 | 
			
		||||
            'webhook_token' => '4u3928u4y392',
 | 
			
		||||
            'private_key_id' => $private_key_2->id,
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'private_key_id' => 2,
 | 
			
		||||
            'team_id' => 0
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\PrivateKey;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class PrivateKeySeeder extends Seeder
 | 
			
		||||
@@ -13,10 +12,9 @@ class PrivateKeySeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $team_1 = Team::find(0);
 | 
			
		||||
        PrivateKey::create([
 | 
			
		||||
            "id" => 0,
 | 
			
		||||
            "team_id" => $team_1->id,
 | 
			
		||||
            "team_id" => 0,
 | 
			
		||||
            "name" => "Testing-host",
 | 
			
		||||
            "description" => "This is a test docker container",
 | 
			
		||||
            "private_key" => "-----BEGIN OPENSSH PRIVATE KEY-----
 | 
			
		||||
@@ -30,7 +28,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
 | 
			
		||||
 | 
			
		||||
        ]);
 | 
			
		||||
        PrivateKey::create([
 | 
			
		||||
            "team_id" => $team_1->id,
 | 
			
		||||
            "team_id" => 0,
 | 
			
		||||
            "name" => "development-github-app",
 | 
			
		||||
            "description" => "This is the key for using the development GitHub app",
 | 
			
		||||
            "private_key" => "-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
@@ -63,7 +61,7 @@ a1C8EDKapCw5hAhizEFOUQKOygL8Ipn+tmEUkORYdZ8Q8cWFCv9nIw==
 | 
			
		||||
            "is_git_related" => true
 | 
			
		||||
        ]);
 | 
			
		||||
        PrivateKey::create([
 | 
			
		||||
            "team_id" => $team_1->id,
 | 
			
		||||
            "team_id" => 0,
 | 
			
		||||
            "name" => "development-gitlab-app",
 | 
			
		||||
            "description" => "This is the key for using the development Gitlab app",
 | 
			
		||||
            "private_key" => "asdf"
 | 
			
		||||
 
 | 
			
		||||
@@ -3,18 +3,16 @@
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Project;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class ProjectSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $root_team = Team::find(0);
 | 
			
		||||
        Project::create([
 | 
			
		||||
            'name' => "My first project",
 | 
			
		||||
            'description' => "This is a test project in development",
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,14 @@
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Project;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class ProjectSettingSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $first_project = Project::find(1);
 | 
			
		||||
//        $first_project = Project::find(1);
 | 
			
		||||
        // $first_project->settings->wildcard_domain = 'wildcard.example.com';
 | 
			
		||||
        $first_project->settings->save();
 | 
			
		||||
//        $first_project->settings->save();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,15 @@ class ScheduledDatabaseBackupSeeder extends Seeder
 | 
			
		||||
            'number_of_backups_locally' => 2,
 | 
			
		||||
            'database_id' => 1,
 | 
			
		||||
            'database_type' => 'App\Models\StandalonePostgresql',
 | 
			
		||||
            's3_storage_id' => 1,
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
        ScheduledDatabaseBackup::create([
 | 
			
		||||
            'enabled' => true,
 | 
			
		||||
            'frequency' => '* * * * *',
 | 
			
		||||
            'number_of_backups_locally' => 3,
 | 
			
		||||
            'database_id' => 1,
 | 
			
		||||
            'database_type' => 'App\Models\StandalonePostgresql',
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,36 +2,24 @@
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\PrivateKey;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class ServerSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $root_team = Team::find(0);
 | 
			
		||||
        $private_key_1 = PrivateKey::find(0);
 | 
			
		||||
 | 
			
		||||
        Server::create([
 | 
			
		||||
            'id' => 0,
 | 
			
		||||
            'name' => "testing-local-docker-container",
 | 
			
		||||
            'description' => "This is a test docker container",
 | 
			
		||||
            'ip' => "coolify-testing-host",
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'private_key_id' => $private_key_1->id,
 | 
			
		||||
            'team_id' => 0,
 | 
			
		||||
            'private_key_id' => 0,
 | 
			
		||||
            // 'proxy' => ServerMetadata::from([
 | 
			
		||||
            //     'type' => ProxyTypes::TRAEFIK_V2->value,
 | 
			
		||||
            //     'status' => ProxyStatus::EXITED->value
 | 
			
		||||
            // ]),
 | 
			
		||||
        ]);
 | 
			
		||||
        Server::create([
 | 
			
		||||
            'name' => "testing-local-docker-container-2",
 | 
			
		||||
            'description' => "This is a test docker container",
 | 
			
		||||
            'ip' => "coolify-testing-host-2",
 | 
			
		||||
            'team_id' => $root_team->id,
 | 
			
		||||
            'private_key_id' => $private_key_1->id
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,11 +18,5 @@ class ServerSettingSeeder extends Seeder
 | 
			
		||||
        $server_2->settings->is_usable = true;
 | 
			
		||||
        $server_2->settings->is_reachable = true;
 | 
			
		||||
        $server_2->settings->save();
 | 
			
		||||
 | 
			
		||||
        $server_3 = Server::find(1)->load(['settings']);
 | 
			
		||||
        $server_3->settings->is_part_of_swarm = false;
 | 
			
		||||
        $server_2->settings->is_usable = false;
 | 
			
		||||
        $server_3->settings->is_reachable = false;
 | 
			
		||||
        $server_3->settings->save();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Environment;
 | 
			
		||||
use App\Models\StandaloneDocker;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class ServiceSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the database seeds.
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $environment_1 = Environment::find(1);
 | 
			
		||||
        $standalone_docker_1 = StandaloneDocker::find(1);
 | 
			
		||||
        // Service::create([
 | 
			
		||||
        //     'id' => 1,
 | 
			
		||||
        //     'name'=> "My first service",
 | 
			
		||||
        //     'environment_id' => $environment_1->id,
 | 
			
		||||
        //     'destination_id' => $standalone_docker_1->id,
 | 
			
		||||
        //     'destination_type' => StandaloneDocker::class,
 | 
			
		||||
        // ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Destination;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\StandaloneDocker;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
@@ -14,11 +13,11 @@ class StandaloneDockerSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $server_1 = Server::find(0);
 | 
			
		||||
        StandaloneDocker::create([
 | 
			
		||||
            'id' => 0,
 | 
			
		||||
            'name' => 'Standalone Docker 1',
 | 
			
		||||
            'network' => 'coolify',
 | 
			
		||||
            'server_id' => $server_1->id,
 | 
			
		||||
            'server_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ class StandalonePostgresqlSeeder extends Seeder
 | 
			
		||||
            'description' => 'Local PostgreSQL for testing',
 | 
			
		||||
            'postgres_password' => 'postgres',
 | 
			
		||||
            'environment_id' => 1,
 | 
			
		||||
            'destination_id' => 1,
 | 
			
		||||
            'destination_id' => 0,
 | 
			
		||||
            'destination_type' => StandaloneDocker::class,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Destination;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\SwarmDocker;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
@@ -14,10 +13,9 @@ class SwarmDockerSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $server_2 = Server::find(1);
 | 
			
		||||
        SwarmDocker::create([
 | 
			
		||||
            'name' => 'Swarm Docker 1',
 | 
			
		||||
            'server_id' => $server_2->id,
 | 
			
		||||
            'server_id' => 1,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,14 +33,14 @@ services:
 | 
			
		||||
      POSTGRES_DB: "${DB_DATABASE:-coolify}"
 | 
			
		||||
      POSTGRES_HOST_AUTH_METHOD: "trust"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./_data/_volumes/database/:/var/lib/postgresql/data
 | 
			
		||||
      - /data/coolify/_volumes/database/:/var/lib/postgresql/data
 | 
			
		||||
  redis:
 | 
			
		||||
    ports:
 | 
			
		||||
      - "${FORWARD_REDIS_PORT:-6379}:6379"
 | 
			
		||||
    env_file:
 | 
			
		||||
      - .env
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./_data/_volumes/redis/:/data
 | 
			
		||||
      - /data/coolify/_volumes/redis/:/data
 | 
			
		||||
  vite:
 | 
			
		||||
    image: node:19
 | 
			
		||||
    working_dir: /var/www/html
 | 
			
		||||
@@ -54,13 +54,7 @@ services:
 | 
			
		||||
    container_name: coolify-testing-host
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /var/run/docker.sock:/var/run/docker.sock
 | 
			
		||||
      - "./_data/_servers/testing-local-docker-container:/data/coolify"
 | 
			
		||||
  testing-host-2:
 | 
			
		||||
    <<: *testing-host-base
 | 
			
		||||
    container_name: coolify-testing-host-2
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /var/run/docker.sock:/var/run/docker.sock
 | 
			
		||||
      - "./_data/_servers/testing-local-docker-container-2:/data/coolify"
 | 
			
		||||
      - /data/coolify/:/data/coolify
 | 
			
		||||
  mailpit:
 | 
			
		||||
    image: "axllent/mailpit:latest"
 | 
			
		||||
    container_name: coolify-mail
 | 
			
		||||
@@ -80,13 +74,6 @@ services:
 | 
			
		||||
      MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}"
 | 
			
		||||
      MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./_data/_volumes/minio/:/data
 | 
			
		||||
      - /data/coolify/_volumes/minio/:/data
 | 
			
		||||
    networks:
 | 
			
		||||
      - coolify
 | 
			
		||||
  # buggregator:
 | 
			
		||||
  #   image: ghcr.io/buggregator/server:latest
 | 
			
		||||
  #   container_name: coolify-debug
 | 
			
		||||
  #   ports:
 | 
			
		||||
  #     - 8001:8000
 | 
			
		||||
  #   networks:
 | 
			
		||||
  #     - coolify
 | 
			
		||||
 
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
FROM alpine:3.17
 | 
			
		||||
 | 
			
		||||
ARG TARGETPLATFORM
 | 
			
		||||
# https://download.docker.com/linux/static/stable/
 | 
			
		||||
ARG DOCKER_VERSION=23.0.6
 | 
			
		||||
# https://github.com/docker/compose/releases
 | 
			
		||||
ARG DOCKER_COMPOSE_VERSION=2.18.1
 | 
			
		||||
# https://github.com/docker/buildx/releases
 | 
			
		||||
ARG DOCKER_BUILDX_VERSION=0.10.5
 | 
			
		||||
# https://github.com/buildpacks/pack/releases
 | 
			
		||||
ARG PACK_VERSION=0.29.0
 | 
			
		||||
# https://github.com/railwayapp/nixpacks/releases
 | 
			
		||||
ARG NIXPACKS_VERSION=1.12.0
 | 
			
		||||
 | 
			
		||||
USER root
 | 
			
		||||
WORKDIR /artifacts
 | 
			
		||||
RUN apk add --no-cache bash curl git git-lfs openssh-client tar tini
 | 
			
		||||
RUN mkdir -p ~/.docker/cli-plugins
 | 
			
		||||
RUN if [[ ${TARGETPLATFORM} == 'linux/amd64' ]]; then \
 | 
			
		||||
    curl -sSL https://github.com/docker/buildx/releases/download/v${DOCKER_BUILDX_VERSION}/buildx-v${DOCKER_BUILDX_VERSION}.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx && \
 | 
			
		||||
    curl -sSL https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose && \
 | 
			
		||||
    (curl -sSL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz | tar -C /usr/bin/ --no-same-owner -xzv --strip-components=1 docker/docker) && \
 | 
			
		||||
    (curl -sSL https://github.com/buildpacks/pack/releases/download/v${PACK_VERSION}/pack-v${PACK_VERSION}-linux.tgz | tar -C /usr/local/bin/ --no-same-owner -xzv pack) && \
 | 
			
		||||
    curl -sSL https://nixpacks.com/install.sh | bash && \
 | 
			
		||||
    chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack /root/.docker/cli-plugins/docker-buildx \
 | 
			
		||||
    ;fi
 | 
			
		||||
 | 
			
		||||
RUN if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
 | 
			
		||||
    curl -sSL https://github.com/docker/buildx/releases/download/v${DOCKER_BUILDX_VERSION}/buildx-v${DOCKER_BUILDX_VERSION}.linux-arm64 -o ~/.docker/cli-plugins/docker-buildx && \
 | 
			
		||||
    curl -sSL https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-linux-aarch64 -o ~/.docker/cli-plugins/docker-compose && \
 | 
			
		||||
    (curl -sSL https://download.docker.com/linux/static/stable/aarch64/docker-${DOCKER_VERSION}.tgz | tar -C /usr/bin/ --no-same-owner -xzv --strip-components=1 docker/docker) && \
 | 
			
		||||
    (curl -sSL https://github.com/buildpacks/pack/releases/download/v${PACK_VERSION}/pack-v${PACK_VERSION}-linux-arm64.tgz | tar -C /usr/local/bin/ --no-same-owner -xzv pack) && \
 | 
			
		||||
    curl -sSL https://nixpacks.com/install.sh | bash && \
 | 
			
		||||
    chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack /root/.docker/cli-plugins/docker-buildx \
 | 
			
		||||
    ;fi
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/sbin/tini", "--"]
 | 
			
		||||
CMD ["sh", "-c", "while true; do sleep 1; done"]
 | 
			
		||||
 | 
			
		||||
@@ -15,4 +15,9 @@
 | 
			
		||||
        @if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model.defer={{ $id }} @endif>
 | 
			
		||||
        {{ $slot }}
 | 
			
		||||
    </select>
 | 
			
		||||
    @error($id)
 | 
			
		||||
    <label class="label">
 | 
			
		||||
        <span class="text-red-500 label-text-alt">{{ $message }}</span>
 | 
			
		||||
    </label>
 | 
			
		||||
    @enderror
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
<div class="pb-6">
 | 
			
		||||
<div class="pb-5">
 | 
			
		||||
    <h1>Settings</h1>
 | 
			
		||||
    <div class="pt-2 pb-10 ">Instance wide settings for Coolify.</div>
 | 
			
		||||
    <div class="pt-2 pb-10">Instance wide settings for Coolify.</div>
 | 
			
		||||
    <nav class="navbar-main">
 | 
			
		||||
        <a class="{{ request()->routeIs('settings.configuration') ? 'text-white' : '' }}"
 | 
			
		||||
           href="{{ route('settings.configuration') }}">
 | 
			
		||||
            <button>Configuration</button>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a class="{{ request()->routeIs('settings.emails') ? 'text-white' : '' }}"
 | 
			
		||||
           href="{{ route('settings.emails') }}">
 | 
			
		||||
            <button>SMTP</button>
 | 
			
		||||
        </a>
 | 
			
		||||
        @if (is_cloud())
 | 
			
		||||
            <a class="{{ request()->routeIs('settings.license') ? 'text-white' : '' }}"
 | 
			
		||||
               href="{{ route('settings.license') }}">
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,24 @@
 | 
			
		||||
        <x-forms.button type="submit">
 | 
			
		||||
            Save
 | 
			
		||||
        </x-forms.button>
 | 
			
		||||
        <x-forms.button isError wire:click="delete">Delete</x-forms.button>
 | 
			
		||||
        @if ($backup->database_id !== 0)
 | 
			
		||||
            <x-forms.button isError wire:click="delete">Delete</x-forms.button>
 | 
			
		||||
        @endif
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="flex py-2 gap-10">
 | 
			
		||||
        <x-forms.checkbox instantSave label="Enabled" id="backup.enabled"/>
 | 
			
		||||
        <x-forms.checkbox instantSave label="Save to S3" id="backup.save_s3"/>
 | 
			
		||||
    <div class="w-32 pb-2">
 | 
			
		||||
        <x-forms.checkbox instantSave label="Backup Enabled" id="backup.enabled"/>
 | 
			
		||||
        <x-forms.checkbox instantSave label="S3 Enabled" id="backup.save_s3"/>
 | 
			
		||||
    </div>
 | 
			
		||||
    @if($backup->save_s3)
 | 
			
		||||
        <div class="pb-6">
 | 
			
		||||
            <x-forms.select id="backup.s3_storage_id" label="S3 Storage" required>
 | 
			
		||||
                <option value="default" disabled>Select a S3 storage</option>
 | 
			
		||||
                @foreach($s3s as $s3)
 | 
			
		||||
                    <option value="{{ $s3->id }}">{{ $s3->name }}</option>
 | 
			
		||||
                @endforeach
 | 
			
		||||
            </x-forms.select>
 | 
			
		||||
        </div>
 | 
			
		||||
    @endif
 | 
			
		||||
    <div class="flex gap-2">
 | 
			
		||||
        <x-forms.input label="Frequency" id="backup.frequency"/>
 | 
			
		||||
        <x-forms.input label="Number of backups to keep (locally)" id="backup.number_of_backups_locally"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,14 @@
 | 
			
		||||
<dialog id="createScheduledBackup" class="modal">
 | 
			
		||||
    <form method="dialog" class="flex flex-col gap-2 rounded modal-box" wire:submit.prevent='submit'>
 | 
			
		||||
        <h3>Details</h3>
 | 
			
		||||
        <x-forms.input placeholder="1 * * * *" id="frequency" label="Frequency" required/>
 | 
			
		||||
        <x-forms.checkbox id="save_s3" label="Save to preconfigured S3"/>
 | 
			
		||||
        <h3>S3 Storage</h3>
 | 
			
		||||
        <x-forms.checkbox id="save_s3" label="Save to S3"/>
 | 
			
		||||
        <x-forms.select label="S3 Storages" id="selected_storage_id">
 | 
			
		||||
            @foreach($s3s as $s3)
 | 
			
		||||
                <option value="{{ $s3->id }}">{{ $s3->name }}</option>
 | 
			
		||||
            @endforeach
 | 
			
		||||
        </x-forms.select>
 | 
			
		||||
        <x-forms.button onclick="createScheduledBackup.close()" type="submit">
 | 
			
		||||
            Save
 | 
			
		||||
        </x-forms.button>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								resources/views/livewire/settings/backup.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								resources/views/livewire/settings/backup.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
<div>
 | 
			
		||||
    <div class="flex flex-col">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
            <h2>Backup</h2>
 | 
			
		||||
            @if(isset($database))
 | 
			
		||||
                <x-forms.button type="submit" wire:click="submit">
 | 
			
		||||
                    Save
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
                <x-forms.button wire:click="backup_now">Backup Now</x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pb-4">Backup your Coolify instance settings</div>
 | 
			
		||||
        <div>
 | 
			
		||||
            @if(isset($database))
 | 
			
		||||
                <div class="flex flex-col gap-3 pb-4">
 | 
			
		||||
                    <div class="flex gap-2">
 | 
			
		||||
                        <x-forms.input label="UUID" readonly id="database.uuid"/>
 | 
			
		||||
                        <x-forms.input label="Name" readonly id="database.name"/>
 | 
			
		||||
                        <x-forms.input label="Description" id="database.description"/>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex gap-2">
 | 
			
		||||
                        <x-forms.input label="User" readonly id="database.postgres_user"/>
 | 
			
		||||
                        <x-forms.input type="password" label="Password" readonly id="database.postgres_password"/>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s"/>
 | 
			
		||||
            @else
 | 
			
		||||
                To configure automatic backup for your Coolify instance, you first need to add as a database resource
 | 
			
		||||
                into Coolify.
 | 
			
		||||
                <x-forms.button wire:click="add_coolify_database">Add Database</x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
<div>
 | 
			
		||||
    <form wire:submit.prevent='submit' class="flex flex-col">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
            <h3>Configuration</h3>
 | 
			
		||||
            <h2>Configuration</h2>
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
                Save
 | 
			
		||||
            </x-forms.button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="">General configuration for your Coolify instance.</div>
 | 
			
		||||
 | 
			
		||||
        <div class="flex flex-col gap-2 pt-4">
 | 
			
		||||
            <div class="flex gap-2 w-96">
 | 
			
		||||
                <x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
    </dialog>
 | 
			
		||||
    <form wire:submit.prevent='submit' class="flex flex-col pb-10">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
            <h3>Transactional Emails</h3>
 | 
			
		||||
            <h2>Transactional Emails</h2>
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
                Save
 | 
			
		||||
            </x-forms.button>
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pt-2 pb-4 ">SMTP settings for password resets, invitations, etc.</div>
 | 
			
		||||
        <div class="pb-4 ">SMTP settings for password resets, invitations, etc.</div>
 | 
			
		||||
        <div class="w-32 pb-4">
 | 
			
		||||
            <x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled"/>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,7 @@
 | 
			
		||||
            </x-forms.button>
 | 
			
		||||
        </x-slot:modalSubmit>
 | 
			
		||||
    </x-modal>
 | 
			
		||||
 | 
			
		||||
    <livewire:project.database.create-scheduled-backup :database="$database"/>
 | 
			
		||||
    <livewire:project.database.create-scheduled-backup :database="$database" :s3s="$s3s"/>
 | 
			
		||||
    <div class="pt-6">
 | 
			
		||||
        <div class="flex gap-2 ">
 | 
			
		||||
            <h2 class="pb-4">Scheduled Backups</h2>
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
        </x-slot:modalSubmit>
 | 
			
		||||
    </x-modal>
 | 
			
		||||
    <div class="pt-6">
 | 
			
		||||
        <livewire:project.database.backup-edit :backup="$backup"/>
 | 
			
		||||
        <livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s"/>
 | 
			
		||||
        <h3 class="py-4">Executions</h3>
 | 
			
		||||
        <livewire:project.database.backup-executions :backup="$backup" :executions="$executions"/>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
<x-layout>
 | 
			
		||||
    <h1>Service</h1>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</x-layout>
 | 
			
		||||
@@ -1,4 +1,25 @@
 | 
			
		||||
<x-layout>
 | 
			
		||||
    <x-settings.navbar/>
 | 
			
		||||
    <livewire:settings.configuration :settings="$settings"/>
 | 
			
		||||
    <div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }"
 | 
			
		||||
         class="flex h-full pt-1">
 | 
			
		||||
        <div class="flex flex-col gap-4 min-w-fit">
 | 
			
		||||
            <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>
 | 
			
		||||
            <a :class="activeTab === 'smtp' && 'text-white'"
 | 
			
		||||
               @click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">SMTP</a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="w-full pl-8">
 | 
			
		||||
            <div x-cloak x-show="activeTab === 'general'" class="h-full">
 | 
			
		||||
                <livewire:settings.configuration :settings="$settings"/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div x-cloak x-show="activeTab === 'backup'" class="h-full">
 | 
			
		||||
                <livewire:settings.backup :settings="$settings" :database="$database" :backup="$backup" :s3s="$s3s"/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div x-cloak x-show="activeTab === 'smtp'" class="h-full">
 | 
			
		||||
                <livewire:settings.email :settings="$settings"/>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</x-layout>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
<x-layout>
 | 
			
		||||
    <x-settings.navbar/>
 | 
			
		||||
    <livewire:settings.email :settings="$settings"/>
 | 
			
		||||
</x-layout>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<x-layout-subscription>
 | 
			
		||||
    <x-settings.navbar/>
 | 
			
		||||
    <h3>Resale License</h3>
 | 
			
		||||
    <h2>Resale License</h2>
 | 
			
		||||
    <livewire:check-license/>
 | 
			
		||||
</x-layout-subscription>
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,6 @@ Route::middleware(['auth'])->group(function () {
 | 
			
		||||
    Route::get('/', [Controller::class, 'dashboard'])->name('dashboard');
 | 
			
		||||
    Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription');
 | 
			
		||||
    Route::get('/settings', [Controller::class, 'settings'])->name('settings.configuration');
 | 
			
		||||
    Route::get('/settings/emails', [Controller::class, 'emails'])->name('settings.emails');
 | 
			
		||||
    Route::get('/settings/license', [Controller::class, 'license'])->name('settings.license');
 | 
			
		||||
    Route::get('/profile', fn() => view('profile', ['request' => request()]))->name('profile');
 | 
			
		||||
    Route::get('/team', [Controller::class, 'team'])->name('team.show');
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ function queue {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function horizon {
 | 
			
		||||
    bash spin exec -u webuser coolify php artisan horizon -vvv  
 | 
			
		||||
    bash spin exec -u webuser coolify php artisan horizon -vvv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function schedule {
 | 
			
		||||
@@ -92,8 +92,8 @@ function tinker {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function build:builder {
 | 
			
		||||
    act -W .github/workflows/coolify-builder.yml --secret-file .env.secrets
 | 
			
		||||
function build:helper {
 | 
			
		||||
    act -W .github/workflows/coolify-helper.yml --secret-file .env.secrets
 | 
			
		||||
}
 | 
			
		||||
function default {
 | 
			
		||||
    help
 | 
			
		||||
 
 | 
			
		||||
@@ -15,4 +15,4 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
 | 
			
		||||
# Merge .env and .env.production. New values will be added to .env
 | 
			
		||||
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' > /data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env
 | 
			
		||||
 | 
			
		||||
docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-builder bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
 | 
			
		||||
docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user