Able to backup Coolify itself
This commit is contained in:
		
							parent
							
								
									b7c9810461
								
							
						
					
					
						commit
						61864970c1
					
				
							
								
								
									
										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\Project; | ||||||
| use App\Models\S3Storage; | use App\Models\S3Storage; | ||||||
| use App\Models\Server; | use App\Models\Server; | ||||||
|  | use App\Models\StandalonePostgresql; | ||||||
| use App\Models\TeamInvitation; | use App\Models\TeamInvitation; | ||||||
| use App\Models\User; | use App\Models\User; | ||||||
| use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | ||||||
| @ -60,20 +61,16 @@ class Controller extends BaseController | |||||||
|     { |     { | ||||||
|         if (auth()->user()->isInstanceAdmin()) { |         if (auth()->user()->isInstanceAdmin()) { | ||||||
|             $settings = InstanceSettings::get(); |             $settings = InstanceSettings::get(); | ||||||
|  |             $database = StandalonePostgresql::whereName('coolify-db')->first(); | ||||||
|  |             if ($database) { | ||||||
|  |                 $backup = $database->scheduledBackups->first(); | ||||||
|  |                 $s3s = S3Storage::whereTeamId(0)->get(); | ||||||
|  |             } | ||||||
|             return view('settings.configuration', [ |             return view('settings.configuration', [ | ||||||
|                 'settings' => $settings |                 'settings' => $settings, | ||||||
|             ]); |                 'database' => $database, | ||||||
|         } else { |                 'backup' => $backup ?? null, | ||||||
|             return redirect()->route('dashboard'); |                 's3s' => $s3s ?? [], | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function emails() |  | ||||||
|     { |  | ||||||
|         if (auth()->user()->isInstanceAdmin()) { |  | ||||||
|             $settings = InstanceSettings::get(); |  | ||||||
|             return view('settings.emails', [ |  | ||||||
|                 'settings' => $settings |  | ||||||
|             ]); |             ]); | ||||||
|         } else { |         } else { | ||||||
|             return redirect()->route('dashboard'); |             return redirect()->route('dashboard'); | ||||||
|  | |||||||
| @ -46,7 +46,12 @@ class DatabaseController extends Controller | |||||||
|             return redirect()->route('dashboard'); |             return redirect()->route('dashboard'); | ||||||
|         } |         } | ||||||
|         $executions = collect($backup->executions)->sortByDesc('created_at'); |         $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() |     public function backups() | ||||||
| @ -63,6 +68,9 @@ class DatabaseController extends Controller | |||||||
|         if (!$database) { |         if (!$database) { | ||||||
|             return redirect()->route('dashboard'); |             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 | class BackupEdit extends Component | ||||||
| { | { | ||||||
|     public $backup; |     public $backup; | ||||||
|  |     public $s3s; | ||||||
|     public array $parameters; |     public array $parameters; | ||||||
| 
 | 
 | ||||||
|     protected $rules = [ |     protected $rules = [ | ||||||
| @ -14,17 +15,25 @@ class BackupEdit extends Component | |||||||
|         'backup.frequency' => 'required|string', |         'backup.frequency' => 'required|string', | ||||||
|         'backup.number_of_backups_locally' => 'required|integer|min:1', |         'backup.number_of_backups_locally' => 'required|integer|min:1', | ||||||
|         'backup.save_s3' => 'required|boolean', |         'backup.save_s3' => 'required|boolean', | ||||||
|  |         'backup.s3_storage_id' => 'nullable|integer', | ||||||
|     ]; |     ]; | ||||||
|     protected $validationAttributes = [ |     protected $validationAttributes = [ | ||||||
|         'backup.enabled' => 'Enabled', |         'backup.enabled' => 'Enabled', | ||||||
|         'backup.frequency' => 'Frequency', |         'backup.frequency' => 'Frequency', | ||||||
|         'backup.number_of_backups_locally' => 'Number of Backups Locally', |         'backup.number_of_backups_locally' => 'Number of Backups Locally', | ||||||
|         'backup.save_s3' => 'Save to S3', |         '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() |     public function mount() | ||||||
|     { |     { | ||||||
|         $this->parameters = get_route_parameters(); |         $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() |     public function instantSave() | ||||||
|     { |     { | ||||||
|         $this->backup->save(); |         try { | ||||||
|         $this->backup->refresh(); |             $this->custom_validate(); | ||||||
|         $this->emit('success', 'Backup updated successfully'); |             $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() |     public function submit() | ||||||
|     { |     { | ||||||
|         $isValid = validate_cron_expression($this->backup->frequency); |         ray($this->backup->s3_storage_id); | ||||||
|         if (!$isValid) { |         try { | ||||||
|             $this->emit('error', 'Invalid Cron / Human expression'); |             $this->custom_validate(); | ||||||
|             return; |             $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 $frequency; | ||||||
|     public bool $enabled = true; |     public bool $enabled = true; | ||||||
|     public bool $save_s3 = true; |     public bool $save_s3 = true; | ||||||
|  |     public $s3_storage_id; | ||||||
|  |     public $s3s; | ||||||
| 
 | 
 | ||||||
|     protected $rules = [ |     protected $rules = [ | ||||||
|         'frequency' => 'required|string', |         'frequency' => 'required|string', | ||||||
| @ -27,13 +29,14 @@ class CreateScheduledBackup extends Component | |||||||
|             $this->validate(); |             $this->validate(); | ||||||
|             $isValid = validate_cron_expression($this->frequency); |             $isValid = validate_cron_expression($this->frequency); | ||||||
|             if (!$isValid) { |             if (!$isValid) { | ||||||
|                 $this->emit('error', 'Invalid Cron / Human expression'); |                 $this->emit('error', 'Invalid Cron / Human expression.'); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             ScheduledDatabaseBackup::create([ |             ScheduledDatabaseBackup::create([ | ||||||
|                 'enabled' => true, |                 'enabled' => true, | ||||||
|                 'frequency' => $this->frequency, |                 'frequency' => $this->frequency, | ||||||
|                 'save_s3' => $this->save_s3, |                 'save_s3' => $this->save_s3, | ||||||
|  |                 's3_storage_id' => $this->s3_storage_id, | ||||||
|                 'database_id' => $this->database->id, |                 'database_id' => $this->database->id, | ||||||
|                 'database_type' => $this->database->getMorphClass(), |                 'database_type' => $this->database->getMorphClass(), | ||||||
|                 'team_id' => auth()->user()->currentTeam()->id, |                 '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( |         $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, |                 "hidden" => true, | ||||||
|             ], |             ], | ||||||
|             [ |             [ | ||||||
|  | |||||||
| @ -30,22 +30,25 @@ class DatabaseBackupJob implements ShouldQueue | |||||||
|     public StandalonePostgresql $database; |     public StandalonePostgresql $database; | ||||||
|     public string $database_status; |     public string $database_status; | ||||||
| 
 | 
 | ||||||
|  |     public string|null $container_name = null; | ||||||
|     public ScheduledDatabaseBackupExecution|null $backup_log = null; |     public ScheduledDatabaseBackupExecution|null $backup_log = null; | ||||||
|     public string $backup_status; |     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 int $size = 0; | ||||||
|     public string|null $backup_output = null; |     public string|null $backup_output = null; | ||||||
|     public S3Storage $s3; |     public S3Storage|null $s3 = null; | ||||||
| 
 | 
 | ||||||
|     public function __construct($backup) |     public function __construct($backup) | ||||||
|     { |     { | ||||||
|         $this->backup = $backup; |         $this->backup = $backup; | ||||||
|         $this->team = Team::find($backup->team_id); |         $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->database_type = $this->database->type(); | ||||||
|         $this->server = $this->database->destination->server; |         $this->server = $this->database->destination->server; | ||||||
|         $this->database_status = $this->database->status; |         $this->database_status = $this->database->status; | ||||||
|         $this->s3 = $this->team->s3; |         $this->s3 = $this->backup->s3; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function middleware(): array |     public function middleware(): array | ||||||
| @ -58,16 +61,23 @@ class DatabaseBackupJob implements ShouldQueue | |||||||
|         return $this->backup->id; |         return $this->backup->id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function handle() |     public function handle(): void | ||||||
|     { |     { | ||||||
|         if ($this->database_status !== 'running') { |         if ($this->database_status !== 'running') { | ||||||
|             ray('database not running'); |             ray('database not running'); | ||||||
|             return; |             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([ |         $this->backup_log = ScheduledDatabaseBackupExecution::create([ | ||||||
|             'filename' => $this->backup_filename, |             'filename' => $this->backup_location, | ||||||
|             'scheduled_database_backup_id' => $this->backup->id, |             'scheduled_database_backup_id' => $this->backup->id, | ||||||
|         ]); |         ]); | ||||||
|         if ($this->database_type === 'standalone-postgresql') { |         if ($this->database_type === 'standalone-postgresql') { | ||||||
| @ -76,18 +86,17 @@ class DatabaseBackupJob implements ShouldQueue | |||||||
|         $this->calculate_size(); |         $this->calculate_size(); | ||||||
|         $this->remove_old_backups(); |         $this->remove_old_backups(); | ||||||
|         if ($this->backup->save_s3) { |         if ($this->backup->save_s3) { | ||||||
| //            $this->upload_to_s3();
 |             $this->upload_to_s3(); | ||||||
|         } |         } | ||||||
|         $this->save_backup_logs(); |         $this->save_backup_logs(); | ||||||
|         // TODO: Notify user
 |         // TODO: Notify user
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function backup_standalone_postgresql() |     private function backup_standalone_postgresql(): void | ||||||
|     { |     { | ||||||
|         try { |         try { | ||||||
|             $commands[] = "mkdir -p " . backup_dir(); |             $commands[] = "mkdir -p " . $this->backup_dir; | ||||||
|             $commands[] = "mkdir -p " . backup_dir() . "/{$this->database->uuid}"; |             $commands[] = "docker exec $this->container_name pg_dumpall -U {$this->database->postgres_user} > $this->backup_location"; | ||||||
|             $commands[] = "docker exec {$this->database->uuid} pg_dumpall -U {$this->database->postgres_user} > $this->backup_filename"; |  | ||||||
| 
 | 
 | ||||||
|             $this->backup_output = instant_remote_process($commands, $this->server); |             $this->backup_output = instant_remote_process($commands, $this->server); | ||||||
| 
 | 
 | ||||||
| @ -97,14 +106,14 @@ class DatabaseBackupJob implements ShouldQueue | |||||||
|                 $this->backup_output = null; |                 $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->backup_status = 'success'; | ||||||
|             $this->team->notify(new BackupSuccess($this->backup, $this->database)); |             $this->team->notify(new BackupSuccess($this->backup, $this->database)); | ||||||
|         } catch (Throwable $th) { |         } catch (Throwable $th) { | ||||||
|             $this->backup_status = 'failed'; |             $this->backup_status = 'failed'; | ||||||
|             $this->add_to_backup_output($th->getMessage()); |             $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)); |             $this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output)); | ||||||
|         } finally { |         } finally { | ||||||
|             $this->backup_log->update([ |             $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) { |         if ($this->backup_output) { | ||||||
|             $this->backup_output = $this->backup_output . "\n" . $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) { |         if ($this->backup->number_of_backups_locally === 0) { | ||||||
|             $deletable = $this->backup->executions()->where('status', 'success'); |             $deletable = $this->backup->executions()->where('status', 'success'); | ||||||
| @ -140,17 +149,7 @@ class DatabaseBackupJob implements ShouldQueue | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function save_backup_logs() |     private function upload_to_s3(): void | ||||||
|     { |  | ||||||
|         $this->backup_log->update([ |  | ||||||
|             'status' => $this->backup_status, |  | ||||||
|             'message' => $this->backup_output, |  | ||||||
|             'size' => $this->size, |  | ||||||
|         ]); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private function upload_to_s3() |  | ||||||
|     { |     { | ||||||
|         try { |         try { | ||||||
|             if (is_null($this->s3)) { |             if (is_null($this->s3)) { | ||||||
| @ -161,16 +160,29 @@ class DatabaseBackupJob implements ShouldQueue | |||||||
| //            $region = $this->s3->region;
 | //            $region = $this->s3->region;
 | ||||||
|             $bucket = $this->s3->bucket; |             $bucket = $this->s3->bucket; | ||||||
|             $endpoint = $this->s3->endpoint; |             $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.'); |             $this->add_to_backup_output('Uploaded to S3.'); | ||||||
|  |             ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir); | ||||||
|         } catch (\Throwable $th) { |         } catch (\Throwable $th) { | ||||||
|             $this->add_to_backup_output($th->getMessage()); |             $this->add_to_backup_output($th->getMessage()); | ||||||
|             ray($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 | class Application extends BaseModel | ||||||
| { | { | ||||||
|     protected $fillable = [ |     protected $guarded = []; | ||||||
|         '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 static function booted() |     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); |         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 | class StandaloneDocker extends BaseModel | ||||||
| { | { | ||||||
|     protected $fillable = [ |     protected $guarded = []; | ||||||
|         'name', |  | ||||||
|         'network', |  | ||||||
|         'server_id', |  | ||||||
|     ]; |  | ||||||
| 
 | 
 | ||||||
|     public function applications() |     public function applications() | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -84,8 +84,8 @@ class Team extends Model implements SendsDiscord, SendsEmail | |||||||
|         return $sources; |         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) { |         if ($isDiscordEnabled && $isSubscribedToDiscordEvent) { | ||||||
|             $channels[] = DiscordChannel::class; |             $channels[] = DiscordChannel::class; | ||||||
|         } |         } | ||||||
|         ray($channels); |  | ||||||
|         return $channels; |         return $channels; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -30,6 +30,19 @@ function format_docker_labels_to_json($rawOutput): Collection | |||||||
|         })[0]; |         })[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) | 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); |     $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->timestamp('started_at')->nullable(); | ||||||
|             $table->morphs('destination'); |             $table->morphs('destination'); | ||||||
| 
 | 
 | ||||||
|             $table->foreignId('environment_id'); |             $table->foreignId('environment_id')->nullable(); | ||||||
|             $table->timestamps(); |             $table->timestamps(); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -9,12 +9,14 @@ return new class extends Migration { | |||||||
|     { |     { | ||||||
|         Schema::create('scheduled_database_backups', function (Blueprint $table) { |         Schema::create('scheduled_database_backups', function (Blueprint $table) { | ||||||
|             $table->id(); |             $table->id(); | ||||||
|  |             $table->text('description')->nullable(); | ||||||
|             $table->string('uuid')->unique(); |             $table->string('uuid')->unique(); | ||||||
|             $table->boolean('enabled')->default(true); |             $table->boolean('enabled')->default(true); | ||||||
|             $table->string('save_s3')->default(true); |             $table->boolean('save_s3')->default(true); | ||||||
|             $table->string('frequency'); |             $table->string('frequency'); | ||||||
|             $table->integer('number_of_backups_locally')->default(7); |             $table->integer('number_of_backups_locally')->default(7); | ||||||
|             $table->morphs('database'); |             $table->morphs('database'); | ||||||
|  |             $table->foreignId('s3_storage_id')->nullable(); | ||||||
|             $table->foreignId('team_id'); |             $table->foreignId('team_id'); | ||||||
|             $table->timestamps(); |             $table->timestamps(); | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| 
 | 
 | ||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Data\ApplicationPreview; |  | ||||||
| use App\Models\Application; | use App\Models\Application; | ||||||
| use App\Models\GithubApp; | use App\Models\GithubApp; | ||||||
| use App\Models\StandaloneDocker; | use App\Models\StandaloneDocker; | ||||||
| @ -15,8 +14,6 @@ class ApplicationSeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $github_public_source = GithubApp::where('name', 'Public GitHub')->first(); |  | ||||||
| 
 |  | ||||||
|         Application::create([ |         Application::create([ | ||||||
|             'name' => 'coollabsio/coolify-examples:nodejs-fastify', |             'name' => 'coollabsio/coolify-examples:nodejs-fastify', | ||||||
|             'description' => 'NodeJS Fastify Example', |             'description' => 'NodeJS Fastify Example', | ||||||
| @ -28,9 +25,9 @@ class ApplicationSeeder extends Seeder | |||||||
|             'ports_exposes' => '3000', |             'ports_exposes' => '3000', | ||||||
|             'ports_mappings' => '3000:3000', |             'ports_mappings' => '3000:3000', | ||||||
|             'environment_id' => 1, |             'environment_id' => 1, | ||||||
|             'destination_id' => 1, |             'destination_id' => 0, | ||||||
|             'destination_type' => StandaloneDocker::class, |             'destination_type' => StandaloneDocker::class, | ||||||
|             'source_id' => $github_public_source->id, |             'source_id' => 1, | ||||||
|             'source_type' => GithubApp::class |             '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, |             ApplicationSeeder::class, | ||||||
|             ApplicationSettingsSeeder::class, |             ApplicationSettingsSeeder::class, | ||||||
|             ApplicationPreviewSeeder::class, |             ApplicationPreviewSeeder::class, | ||||||
|             DBSeeder::class, |  | ||||||
|             ServiceSeeder::class, |  | ||||||
|             EnvironmentVariableSeeder::class, |             EnvironmentVariableSeeder::class, | ||||||
|             LocalPersistentVolumeSeeder::class, |             LocalPersistentVolumeSeeder::class, | ||||||
|             S3StorageSeeder::class, |             S3StorageSeeder::class, | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| 
 | 
 | ||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\EnvironmentVariable; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class EnvironmentVariableSeeder extends Seeder | class EnvironmentVariableSeeder extends Seeder | ||||||
| @ -12,11 +11,11 @@ class EnvironmentVariableSeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         EnvironmentVariable::create([ | //        EnvironmentVariable::create([
 | ||||||
|             'key' => 'NODE_ENV', | //            'key' => 'NODE_ENV',
 | ||||||
|             'value' => 'production', | //            'value' => 'production',
 | ||||||
|             'is_build_time' => true, | //            'is_build_time' => true,
 | ||||||
|             'application_id' => 1, | //            'application_id' => 1,
 | ||||||
|         ]); | //        ]);
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ | |||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\GithubApp; | use App\Models\GithubApp; | ||||||
| use App\Models\PrivateKey; |  | ||||||
| use App\Models\Team; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class GithubAppSeeder extends Seeder | class GithubAppSeeder extends Seeder | ||||||
| @ -14,14 +12,12 @@ class GithubAppSeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $root_team = Team::find(0); |  | ||||||
|         $private_key_2 = PrivateKey::find(1); |  | ||||||
|         GithubApp::create([ |         GithubApp::create([ | ||||||
|             'name' => 'Public GitHub', |             'name' => 'Public GitHub', | ||||||
|             'api_url' => 'https://api.github.com', |             'api_url' => 'https://api.github.com', | ||||||
|             'html_url' => 'https://github.com', |             'html_url' => 'https://github.com', | ||||||
|             'is_public' => true, |             'is_public' => true, | ||||||
|             'team_id' => $root_team->id, |             'team_id' => 0, | ||||||
|         ]); |         ]); | ||||||
|         GithubApp::create([ |         GithubApp::create([ | ||||||
|             'name' => 'coolify-laravel-development-public', |             'name' => 'coolify-laravel-development-public', | ||||||
| @ -34,8 +30,8 @@ class GithubAppSeeder extends Seeder | |||||||
|             'client_id' => 'Iv1.220e564d2b0abd8c', |             'client_id' => 'Iv1.220e564d2b0abd8c', | ||||||
|             'client_secret' => '116d1d80289f378410dd70ab4e4b81dd8d2c52b6', |             'client_secret' => '116d1d80289f378410dd70ab4e4b81dd8d2c52b6', | ||||||
|             'webhook_secret' => '326a47b49054f03288f800d81247ec9414d0abf3', |             'webhook_secret' => '326a47b49054f03288f800d81247ec9414d0abf3', | ||||||
|             'private_key_id' => $private_key_2->id, |             'private_key_id' => 1, | ||||||
|             'team_id' => $root_team->id, |             'team_id' => 0, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ | |||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\GitlabApp; | use App\Models\GitlabApp; | ||||||
| use App\Models\PrivateKey; |  | ||||||
| use App\Models\Team; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class GitlabAppSeeder extends Seeder | class GitlabAppSeeder extends Seeder | ||||||
| @ -14,15 +12,13 @@ class GitlabAppSeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $root_team = Team::find(0); |  | ||||||
|         $private_key_2 = PrivateKey::find(2); |  | ||||||
|         GitlabApp::create([ |         GitlabApp::create([ | ||||||
|             'id' => 1, |             'id' => 1, | ||||||
|             'name' => 'Public GitLab', |             'name' => 'Public GitLab', | ||||||
|             'api_url' => 'https://gitlab.com/api/v4', |             'api_url' => 'https://gitlab.com/api/v4', | ||||||
|             'html_url' => 'https://gitlab.com', |             'html_url' => 'https://gitlab.com', | ||||||
|             'is_public' => true, |             'is_public' => true, | ||||||
|             'team_id' => $root_team->id, |             'team_id' => 0, | ||||||
|         ]); |         ]); | ||||||
|         GitlabApp::create([ |         GitlabApp::create([ | ||||||
|             'id' => 2, |             'id' => 2, | ||||||
| @ -35,8 +31,8 @@ class GitlabAppSeeder extends Seeder | |||||||
|             'deploy_key_id' => '1234', |             'deploy_key_id' => '1234', | ||||||
|             'public_key' => 'dfjasiourj', |             'public_key' => 'dfjasiourj', | ||||||
|             'webhook_token' => '4u3928u4y392', |             'webhook_token' => '4u3928u4y392', | ||||||
|             'private_key_id' => $private_key_2->id, |             'private_key_id' => 2, | ||||||
|             'team_id' => $root_team->id, |             'team_id' => 0 | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ | |||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\PrivateKey; | use App\Models\PrivateKey; | ||||||
| use App\Models\Team; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class PrivateKeySeeder extends Seeder | class PrivateKeySeeder extends Seeder | ||||||
| @ -13,10 +12,9 @@ class PrivateKeySeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $team_1 = Team::find(0); |  | ||||||
|         PrivateKey::create([ |         PrivateKey::create([ | ||||||
|             "id" => 0, |             "id" => 0, | ||||||
|             "team_id" => $team_1->id, |             "team_id" => 0, | ||||||
|             "name" => "Testing-host", |             "name" => "Testing-host", | ||||||
|             "description" => "This is a test docker container", |             "description" => "This is a test docker container", | ||||||
|             "private_key" => "-----BEGIN OPENSSH PRIVATE KEY-----
 |             "private_key" => "-----BEGIN OPENSSH PRIVATE KEY-----
 | ||||||
| @ -30,7 +28,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== | |||||||
| 
 | 
 | ||||||
|         ]); |         ]); | ||||||
|         PrivateKey::create([ |         PrivateKey::create([ | ||||||
|             "team_id" => $team_1->id, |             "team_id" => 0, | ||||||
|             "name" => "development-github-app", |             "name" => "development-github-app", | ||||||
|             "description" => "This is the key for using the development GitHub app", |             "description" => "This is the key for using the development GitHub app", | ||||||
|             "private_key" => "-----BEGIN RSA PRIVATE KEY-----
 |             "private_key" => "-----BEGIN RSA PRIVATE KEY-----
 | ||||||
| @ -63,7 +61,7 @@ a1C8EDKapCw5hAhizEFOUQKOygL8Ipn+tmEUkORYdZ8Q8cWFCv9nIw== | |||||||
|             "is_git_related" => true |             "is_git_related" => true | ||||||
|         ]); |         ]); | ||||||
|         PrivateKey::create([ |         PrivateKey::create([ | ||||||
|             "team_id" => $team_1->id, |             "team_id" => 0, | ||||||
|             "name" => "development-gitlab-app", |             "name" => "development-gitlab-app", | ||||||
|             "description" => "This is the key for using the development Gitlab app", |             "description" => "This is the key for using the development Gitlab app", | ||||||
|             "private_key" => "asdf" |             "private_key" => "asdf" | ||||||
|  | |||||||
| @ -3,18 +3,16 @@ | |||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\Project; | use App\Models\Project; | ||||||
| use App\Models\Team; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class ProjectSeeder extends Seeder | class ProjectSeeder extends Seeder | ||||||
| { | { | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $root_team = Team::find(0); |  | ||||||
|         Project::create([ |         Project::create([ | ||||||
|             'name' => "My first project", |             'name' => "My first project", | ||||||
|             'description' => "This is a test project in development", |             'description' => "This is a test project in development", | ||||||
|             'team_id' => $root_team->id, |             'team_id' => 0, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,15 +2,14 @@ | |||||||
| 
 | 
 | ||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\Project; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class ProjectSettingSeeder extends Seeder | class ProjectSettingSeeder extends Seeder | ||||||
| { | { | ||||||
|     public function run(): void |     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->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, |             'number_of_backups_locally' => 2, | ||||||
|             'database_id' => 1, |             'database_id' => 1, | ||||||
|             'database_type' => 'App\Models\StandalonePostgresql', |             '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, |             'team_id' => 0, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -2,36 +2,24 @@ | |||||||
| 
 | 
 | ||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\PrivateKey; |  | ||||||
| use App\Models\Server; | use App\Models\Server; | ||||||
| use App\Models\Team; |  | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| class ServerSeeder extends Seeder | class ServerSeeder extends Seeder | ||||||
| { | { | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $root_team = Team::find(0); |  | ||||||
|         $private_key_1 = PrivateKey::find(0); |  | ||||||
| 
 |  | ||||||
|         Server::create([ |         Server::create([ | ||||||
|             'id' => 0, |             'id' => 0, | ||||||
|             'name' => "testing-local-docker-container", |             'name' => "testing-local-docker-container", | ||||||
|             'description' => "This is a test docker container", |             'description' => "This is a test docker container", | ||||||
|             'ip' => "coolify-testing-host", |             'ip' => "coolify-testing-host", | ||||||
|             'team_id' => $root_team->id, |             'team_id' => 0, | ||||||
|             'private_key_id' => $private_key_1->id, |             'private_key_id' => 0, | ||||||
|             // 'proxy' => ServerMetadata::from([
 |             // 'proxy' => ServerMetadata::from([
 | ||||||
|             //     'type' => ProxyTypes::TRAEFIK_V2->value,
 |             //     'type' => ProxyTypes::TRAEFIK_V2->value,
 | ||||||
|             //     'status' => ProxyStatus::EXITED->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_usable = true; | ||||||
|         $server_2->settings->is_reachable = true; |         $server_2->settings->is_reachable = true; | ||||||
|         $server_2->settings->save(); |         $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; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\Destination; | use App\Models\Destination; | ||||||
| use App\Models\Server; |  | ||||||
| use App\Models\StandaloneDocker; | use App\Models\StandaloneDocker; | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| @ -14,11 +13,11 @@ class StandaloneDockerSeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $server_1 = Server::find(0); |  | ||||||
|         StandaloneDocker::create([ |         StandaloneDocker::create([ | ||||||
|  |             'id' => 0, | ||||||
|             'name' => 'Standalone Docker 1', |             'name' => 'Standalone Docker 1', | ||||||
|             'network' => 'coolify', |             'network' => 'coolify', | ||||||
|             'server_id' => $server_1->id, |             'server_id' => 0, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ class StandalonePostgresqlSeeder extends Seeder | |||||||
|             'description' => 'Local PostgreSQL for testing', |             'description' => 'Local PostgreSQL for testing', | ||||||
|             'postgres_password' => 'postgres', |             'postgres_password' => 'postgres', | ||||||
|             'environment_id' => 1, |             'environment_id' => 1, | ||||||
|             'destination_id' => 1, |             'destination_id' => 0, | ||||||
|             'destination_type' => StandaloneDocker::class, |             'destination_type' => StandaloneDocker::class, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ | |||||||
| namespace Database\Seeders; | namespace Database\Seeders; | ||||||
| 
 | 
 | ||||||
| use App\Models\Destination; | use App\Models\Destination; | ||||||
| use App\Models\Server; |  | ||||||
| use App\Models\SwarmDocker; | use App\Models\SwarmDocker; | ||||||
| use Illuminate\Database\Seeder; | use Illuminate\Database\Seeder; | ||||||
| 
 | 
 | ||||||
| @ -14,10 +13,9 @@ class SwarmDockerSeeder extends Seeder | |||||||
|      */ |      */ | ||||||
|     public function run(): void |     public function run(): void | ||||||
|     { |     { | ||||||
|         $server_2 = Server::find(1); |  | ||||||
|         SwarmDocker::create([ |         SwarmDocker::create([ | ||||||
|             'name' => 'Swarm Docker 1', |             'name' => 'Swarm Docker 1', | ||||||
|             'server_id' => $server_2->id, |             'server_id' => 1, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,14 +33,14 @@ services: | |||||||
|       POSTGRES_DB: "${DB_DATABASE:-coolify}" |       POSTGRES_DB: "${DB_DATABASE:-coolify}" | ||||||
|       POSTGRES_HOST_AUTH_METHOD: "trust" |       POSTGRES_HOST_AUTH_METHOD: "trust" | ||||||
|     volumes: |     volumes: | ||||||
|       - ./_data/_volumes/database/:/var/lib/postgresql/data |       - /data/coolify/_volumes/database/:/var/lib/postgresql/data | ||||||
|   redis: |   redis: | ||||||
|     ports: |     ports: | ||||||
|       - "${FORWARD_REDIS_PORT:-6379}:6379" |       - "${FORWARD_REDIS_PORT:-6379}:6379" | ||||||
|     env_file: |     env_file: | ||||||
|       - .env |       - .env | ||||||
|     volumes: |     volumes: | ||||||
|       - ./_data/_volumes/redis/:/data |       - /data/coolify/_volumes/redis/:/data | ||||||
|   vite: |   vite: | ||||||
|     image: node:19 |     image: node:19 | ||||||
|     working_dir: /var/www/html |     working_dir: /var/www/html | ||||||
| @ -54,13 +54,7 @@ services: | |||||||
|     container_name: coolify-testing-host |     container_name: coolify-testing-host | ||||||
|     volumes: |     volumes: | ||||||
|       - /var/run/docker.sock:/var/run/docker.sock |       - /var/run/docker.sock:/var/run/docker.sock | ||||||
|       - "./_data/_servers/testing-local-docker-container:/data/coolify" |       - /data/coolify/:/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" |  | ||||||
|   mailpit: |   mailpit: | ||||||
|     image: "axllent/mailpit:latest" |     image: "axllent/mailpit:latest" | ||||||
|     container_name: coolify-mail |     container_name: coolify-mail | ||||||
| @ -80,13 +74,6 @@ services: | |||||||
|       MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}" |       MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}" | ||||||
|       MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}" |       MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}" | ||||||
|     volumes: |     volumes: | ||||||
|       - ./_data/_volumes/minio/:/data |       - /data/coolify/_volumes/minio/:/data | ||||||
|     networks: |     networks: | ||||||
|       - coolify |       - 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> |         @if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model.defer={{ $id }} @endif> | ||||||
|         {{ $slot }} |         {{ $slot }} | ||||||
|     </select> |     </select> | ||||||
|  |     @error($id) | ||||||
|  |     <label class="label"> | ||||||
|  |         <span class="text-red-500 label-text-alt">{{ $message }}</span> | ||||||
|  |     </label> | ||||||
|  |     @enderror | ||||||
| </div> | </div> | ||||||
|  | |||||||
| @ -1,15 +1,11 @@ | |||||||
| <div class="pb-6"> | <div class="pb-5"> | ||||||
|     <h1>Settings</h1> |     <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"> |     <nav class="navbar-main"> | ||||||
|         <a class="{{ request()->routeIs('settings.configuration') ? 'text-white' : '' }}" |         <a class="{{ request()->routeIs('settings.configuration') ? 'text-white' : '' }}" | ||||||
|            href="{{ route('settings.configuration') }}"> |            href="{{ route('settings.configuration') }}"> | ||||||
|             <button>Configuration</button> |             <button>Configuration</button> | ||||||
|         </a> |         </a> | ||||||
|         <a class="{{ request()->routeIs('settings.emails') ? 'text-white' : '' }}" |  | ||||||
|            href="{{ route('settings.emails') }}"> |  | ||||||
|             <button>SMTP</button> |  | ||||||
|         </a> |  | ||||||
|         @if (is_cloud()) |         @if (is_cloud()) | ||||||
|             <a class="{{ request()->routeIs('settings.license') ? 'text-white' : '' }}" |             <a class="{{ request()->routeIs('settings.license') ? 'text-white' : '' }}" | ||||||
|                href="{{ route('settings.license') }}"> |                href="{{ route('settings.license') }}"> | ||||||
|  | |||||||
| @ -4,12 +4,24 @@ | |||||||
|         <x-forms.button type="submit"> |         <x-forms.button type="submit"> | ||||||
|             Save |             Save | ||||||
|         </x-forms.button> |         </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> | ||||||
|     <div class="flex py-2 gap-10"> |     <div class="w-32 pb-2"> | ||||||
|         <x-forms.checkbox instantSave label="Enabled" id="backup.enabled"/> |         <x-forms.checkbox instantSave label="Backup Enabled" id="backup.enabled"/> | ||||||
|         <x-forms.checkbox instantSave label="Save to S3" id="backup.save_s3"/> |         <x-forms.checkbox instantSave label="S3 Enabled" id="backup.save_s3"/> | ||||||
|     </div> |     </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"> |     <div class="flex gap-2"> | ||||||
|         <x-forms.input label="Frequency" id="backup.frequency"/> |         <x-forms.input label="Frequency" id="backup.frequency"/> | ||||||
|         <x-forms.input label="Number of backups to keep (locally)" id="backup.number_of_backups_locally"/> |         <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"> | <dialog id="createScheduledBackup" class="modal"> | ||||||
|     <form method="dialog" class="flex flex-col gap-2 rounded modal-box" wire:submit.prevent='submit'> |     <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.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"> |         <x-forms.button onclick="createScheduledBackup.close()" type="submit"> | ||||||
|             Save |             Save | ||||||
|         </x-forms.button> |         </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> | <div> | ||||||
|     <form wire:submit.prevent='submit' class="flex flex-col"> |     <form wire:submit.prevent='submit' class="flex flex-col"> | ||||||
|         <div class="flex items-center gap-2"> |         <div class="flex items-center gap-2"> | ||||||
|             <h3>Configuration</h3> |             <h2>Configuration</h2> | ||||||
|             <x-forms.button type="submit"> |             <x-forms.button type="submit"> | ||||||
|                 Save |                 Save | ||||||
|             </x-forms.button> |             </x-forms.button> | ||||||
|         </div> |         </div> | ||||||
|  |         <div class="">General configuration for your Coolify instance.</div> | ||||||
|  | 
 | ||||||
|         <div class="flex flex-col gap-2 pt-4"> |         <div class="flex flex-col gap-2 pt-4"> | ||||||
|             <div class="flex gap-2 w-96"> |             <div class="flex gap-2 w-96"> | ||||||
|                 <x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io"/> |                 <x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io"/> | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
|     </dialog> |     </dialog> | ||||||
|     <form wire:submit.prevent='submit' class="flex flex-col pb-10"> |     <form wire:submit.prevent='submit' class="flex flex-col pb-10"> | ||||||
|         <div class="flex items-center gap-2"> |         <div class="flex items-center gap-2"> | ||||||
|             <h3>Transactional Emails</h3> |             <h2>Transactional Emails</h2> | ||||||
|             <x-forms.button type="submit"> |             <x-forms.button type="submit"> | ||||||
|                 Save |                 Save | ||||||
|             </x-forms.button> |             </x-forms.button> | ||||||
| @ -23,7 +23,7 @@ | |||||||
|                 </x-forms.button> |                 </x-forms.button> | ||||||
|             @endif |             @endif | ||||||
|         </div> |         </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"> |         <div class="w-32 pb-4"> | ||||||
|             <x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled"/> |             <x-forms.checkbox instantSave id="settings.smtp_enabled" label="Enabled"/> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -11,8 +11,7 @@ | |||||||
|             </x-forms.button> |             </x-forms.button> | ||||||
|         </x-slot:modalSubmit> |         </x-slot:modalSubmit> | ||||||
|     </x-modal> |     </x-modal> | ||||||
| 
 |     <livewire:project.database.create-scheduled-backup :database="$database" :s3s="$s3s"/> | ||||||
|     <livewire:project.database.create-scheduled-backup :database="$database"/> |  | ||||||
|     <div class="pt-6"> |     <div class="pt-6"> | ||||||
|         <div class="flex gap-2 "> |         <div class="flex gap-2 "> | ||||||
|             <h2 class="pb-4">Scheduled Backups</h2> |             <h2 class="pb-4">Scheduled Backups</h2> | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
|         </x-slot:modalSubmit> |         </x-slot:modalSubmit> | ||||||
|     </x-modal> |     </x-modal> | ||||||
|     <div class="pt-6"> |     <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> |         <h3 class="py-4">Executions</h3> | ||||||
|         <livewire:project.database.backup-executions :backup="$backup" :executions="$executions"/> |         <livewire:project.database.backup-executions :backup="$backup" :executions="$executions"/> | ||||||
|     </div> |     </div> | ||||||
|  | |||||||
| @ -1,5 +0,0 @@ | |||||||
| <x-layout> |  | ||||||
|     <h1>Service</h1> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| </x-layout> |  | ||||||
| @ -1,4 +1,25 @@ | |||||||
| <x-layout> | <x-layout> | ||||||
|     <x-settings.navbar/> |     <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> | </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-layout-subscription> | ||||||
|     <x-settings.navbar/> |     <x-settings.navbar/> | ||||||
|     <h3>Resale License</h3> |     <h2>Resale License</h2> | ||||||
|     <livewire:check-license/> |     <livewire:check-license/> | ||||||
| </x-layout-subscription> | </x-layout-subscription> | ||||||
|  | |||||||
| @ -94,7 +94,6 @@ Route::middleware(['auth'])->group(function () { | |||||||
|     Route::get('/', [Controller::class, 'dashboard'])->name('dashboard'); |     Route::get('/', [Controller::class, 'dashboard'])->name('dashboard'); | ||||||
|     Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription'); |     Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription'); | ||||||
|     Route::get('/settings', [Controller::class, 'settings'])->name('settings.configuration'); |     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('/settings/license', [Controller::class, 'license'])->name('settings.license'); | ||||||
|     Route::get('/profile', fn() => view('profile', ['request' => request()]))->name('profile'); |     Route::get('/profile', fn() => view('profile', ['request' => request()]))->name('profile'); | ||||||
|     Route::get('/team', [Controller::class, 'team'])->name('team.show'); |     Route::get('/team', [Controller::class, 'team'])->name('team.show'); | ||||||
|  | |||||||
| @ -92,8 +92,8 @@ function tinker { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function build:builder { | function build:helper { | ||||||
|     act -W .github/workflows/coolify-builder.yml --secret-file .env.secrets |     act -W .github/workflows/coolify-helper.yml --secret-file .env.secrets | ||||||
| } | } | ||||||
| function default { | function default { | ||||||
|     help |     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 | # 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 | 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" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user