mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-01 05:09:23 +00:00
Compare commits
25 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31cbd1602d | ||
|
|
dd5723d596 | ||
|
|
620f26a6f1 | ||
|
|
540717e809 | ||
|
|
d446cd4103 | ||
|
|
7d1a76570c | ||
|
|
e18766ec21 | ||
|
|
3d0354cf7e | ||
|
|
af5b9fced1 | ||
|
|
ab5202515e | ||
|
|
f863db7ea5 | ||
|
|
3adc0bdd6e | ||
|
|
97027875bf | ||
|
|
fd9c13009f | ||
|
|
46a72fac47 | ||
|
|
5d6ee04991 | ||
|
|
d523becb29 | ||
|
|
41672f75d0 | ||
|
|
acd8541e68 | ||
|
|
ed6af777a4 | ||
|
|
05f162f4e8 | ||
|
|
5e0adc3777 | ||
|
|
7d06fc4403 | ||
|
|
0e1bcceb8e | ||
|
|
a922f2fedf |
28
CONTRIBUTION.md
Normal file
28
CONTRIBUTION.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
> "First, thanks for considering to contribute to my project.
|
||||||
|
It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
||||||
|
|
||||||
|
You can ask for guidance anytime on our
|
||||||
|
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
||||||
|
|
||||||
|
|
||||||
|
## 1) Setup your development environment
|
||||||
|
|
||||||
|
- You need to have Docker Engine (or equivalent) [installed](https://docs.docker.com/engine/install/) on your system.
|
||||||
|
- For better DX, install [Spin](https://serversideup.net/open-source/spin/).
|
||||||
|
|
||||||
|
## 2) Set your environment variables
|
||||||
|
|
||||||
|
- Copy [.env.development.example](./.env.development.example) to .env.
|
||||||
|
- If necessary, set `USERID` & `GROUPID` accordingly (read in .env file).
|
||||||
|
|
||||||
|
## 3) Start & setup Coolify
|
||||||
|
|
||||||
|
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||||
|
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
||||||
|
|
||||||
|
## 4) Start development
|
||||||
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
|
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
||||||
@@ -4,12 +4,14 @@ namespace App\Actions\Service;
|
|||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartService
|
class StartService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
$network = $service->destination->network;
|
||||||
$service->saveComposeConfigs();
|
$service->saveComposeConfigs();
|
||||||
$commands[] = "cd " . $service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||||
@@ -21,6 +23,11 @@ class StartService
|
|||||||
$commands[] = "echo '####### Starting containers.'";
|
$commands[] = "echo '####### Starting containers.'";
|
||||||
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
||||||
|
$compose = data_get($service,'docker_compose',[]);
|
||||||
|
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
||||||
|
foreach($serviceNames as $serviceName => $serviceConfig){
|
||||||
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} 2>/dev/null || true";
|
||||||
|
}
|
||||||
$activity = remote_process($commands, $service->server);
|
$activity = remote_process($commands, $service->server);
|
||||||
return $activity;
|
return $activity;
|
||||||
}
|
}
|
||||||
|
|||||||
49
app/Console/Commands/ResetRootPassword.php
Normal file
49
app/Console/Commands/ResetRootPassword.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\password;
|
||||||
|
|
||||||
|
class ResetRootPassword extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:reset-root-password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Reset Root Password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->info('You are about to reset the root password.');
|
||||||
|
$password = password('Give me a new password for root user: ');
|
||||||
|
$passwordAgain = password('Again');
|
||||||
|
if ($password != $passwordAgain) {
|
||||||
|
$this->error('Passwords do not match.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->info('Updating root password...');
|
||||||
|
try {
|
||||||
|
User::find(0)->update(['password' => Hash::make($password)]);
|
||||||
|
$this->info('Root password updated successfully.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to update root password.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ use App\Models\EnvironmentVariable;
|
|||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use App\Models\StandaloneDocker;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class ProjectController extends Controller
|
||||||
@@ -75,11 +75,14 @@ class ProjectController extends Controller
|
|||||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||||
}
|
}
|
||||||
if ($oneClickService) {
|
if ($oneClickService) {
|
||||||
|
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||||
$service = Service::create([
|
$service = Service::create([
|
||||||
'name' => "$oneClickServiceName-" . Str::random(10),
|
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||||
'docker_compose_raw' => base64_decode($oneClickService),
|
'docker_compose_raw' => base64_decode($oneClickService),
|
||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'server_id' => (int) $server_id,
|
'server_id' => (int) $server_id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
]);
|
]);
|
||||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||||
$service->save();
|
$service->save();
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
if (data_get($this->application, 'dockerfile')) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port) {
|
if ($port && !$this->application->ports_exposes) {
|
||||||
$this->application->ports_exposes = $port;
|
$this->application->ports_exposes = $port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ class DockerCompose extends Component
|
|||||||
- type: volume
|
- type: volume
|
||||||
source: mydata
|
source: mydata
|
||||||
target: /data
|
target: /data
|
||||||
volume:
|
|
||||||
nocopy: true
|
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ./var/lib/ghost/data
|
source: ./var/lib/ghost/data
|
||||||
target: /data
|
target: /data
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => $destination->id,
|
'destination_id' => $destination->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
'source_id' => 0,
|
'source_id' => 0,
|
||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class Navbar extends Component
|
|||||||
}
|
}
|
||||||
public function serviceStatusUpdated()
|
public function serviceStatusUpdated()
|
||||||
{
|
{
|
||||||
ray('serviceStatusUpdated');
|
|
||||||
$this->check_status();
|
$this->check_status();
|
||||||
}
|
}
|
||||||
public function check_status()
|
public function check_status()
|
||||||
|
|||||||
40
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
40
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class GetLogs extends Component
|
||||||
|
{
|
||||||
|
public string $outputs = '';
|
||||||
|
public string $errors = '';
|
||||||
|
public Server $server;
|
||||||
|
public ?string $container = null;
|
||||||
|
public ?bool $streamLogs = false;
|
||||||
|
public int $numberOfLines = 100;
|
||||||
|
public function doSomethingWithThisChunkOfOutput($output)
|
||||||
|
{
|
||||||
|
$this->outputs .= $output;
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function getLogs($refresh = false)
|
||||||
|
{
|
||||||
|
if ($this->container) {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
||||||
|
if ($refresh) {
|
||||||
|
$this->outputs = '';
|
||||||
|
}
|
||||||
|
Process::run($sshCommand, function (string $type, string $output) {
|
||||||
|
$this->doSomethingWithThisChunkOfOutput($output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.get-logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ class HealthChecks extends Component
|
|||||||
|
|
||||||
public $resource;
|
public $resource;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
'resource.health_check_enabled' => 'boolean',
|
||||||
'resource.health_check_path' => 'string',
|
'resource.health_check_path' => 'string',
|
||||||
'resource.health_check_port' => 'nullable|string',
|
'resource.health_check_port' => 'nullable|string',
|
||||||
'resource.health_check_host' => 'string',
|
'resource.health_check_host' => 'string',
|
||||||
@@ -22,12 +23,19 @@ class HealthChecks extends Component
|
|||||||
'resource.health_check_start_period' => 'integer',
|
'resource.health_check_start_period' => 'integer',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->resource->save();
|
||||||
|
$this->emit('success', 'Health check updated.');
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->resource->save();
|
$this->resource->save();
|
||||||
$this->emit('saved');
|
$this->emit('success', 'Health check updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
53
app/Http/Livewire/Project/Shared/Logs.php
Normal file
53
app/Http/Livewire/Project/Shared/Logs.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Logs extends Component
|
||||||
|
{
|
||||||
|
public ?string $type = null;
|
||||||
|
public Application|StandalonePostgresql|Service $resource;
|
||||||
|
public Server $server;
|
||||||
|
public ?string $container = null;
|
||||||
|
public $parameters;
|
||||||
|
public $query;
|
||||||
|
public $status;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
$this->type = 'application';
|
||||||
|
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
$this->container = data_get($containers[0], 'Names');
|
||||||
|
}
|
||||||
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
|
$this->type = 'database';
|
||||||
|
$this->resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$this->container = $this->resource->uuid;
|
||||||
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
|
$this->type = 'service';
|
||||||
|
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->server;
|
||||||
|
$this->container = data_get($this->parameters, 'service_name') . '-' . $this->resource->uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Add extends Component
|
class Add extends Component
|
||||||
{
|
{
|
||||||
|
public $uuid;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $mount_path;
|
public string $mount_path;
|
||||||
@@ -31,8 +32,9 @@ class Add extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
$name = $this->uuid . '-' . $this->name;
|
||||||
$this->emitUp('submit', [
|
$this->emitUp('submit', [
|
||||||
'name' => $this->name,
|
'name' => $name,
|
||||||
'mount_path' => $this->mount_path,
|
'mount_path' => $this->mount_path,
|
||||||
'host_path' => $this->host_path,
|
'host_path' => $this->host_path,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ class Show extends Component
|
|||||||
public LocalPersistentVolume $storage;
|
public LocalPersistentVolume $storage;
|
||||||
public bool $isReadOnly = false;
|
public bool $isReadOnly = false;
|
||||||
public ?string $modalId = null;
|
public ?string $modalId = null;
|
||||||
public ?string $realName = null;
|
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'storage.name' => 'required|string',
|
'storage.name' => 'required|string',
|
||||||
@@ -26,11 +25,6 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if ($this->storage->resource_type === 'App\Models\ServiceApplication' || $this->storage->resource_type === 'App\Models\ServiceDatabase') {
|
|
||||||
$this->realName = "{$this->storage->service->service->uuid}_{$this->storage->name}";
|
|
||||||
} else {
|
|
||||||
$this->realName = $this->storage->name;
|
|
||||||
}
|
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
if (config('coolify.waitlist') == false) {
|
||||||
|
return redirect()->route('register');
|
||||||
|
}
|
||||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||||
$this->users = User::count();
|
$this->users = User::count();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
|
|||||||
@@ -303,6 +303,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
private function health_check()
|
private function health_check()
|
||||||
{
|
{
|
||||||
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
|
$this->newVersionIsHealthy = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
ray('New container name: ', $this->container_name);
|
ray('New container name: ', $this->container_name);
|
||||||
if ($this->container_name) {
|
if ($this->container_name) {
|
||||||
$counter = 0;
|
$counter = 0;
|
||||||
@@ -375,9 +379,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
{
|
{
|
||||||
$pull = "--pull=always";
|
$pull = "--pull=always";
|
||||||
if (isDev()) {
|
|
||||||
$pull = "--pull=never";
|
|
||||||
}
|
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||||
|
|
||||||
@@ -573,6 +574,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
|
data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck');
|
||||||
|
}
|
||||||
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
|
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
|
||||||
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
|
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,4 +231,12 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
public function isHealthcheckDisabled(): bool
|
||||||
|
{
|
||||||
|
if (data_get($this, 'dockerfile') || data_get($this, 'build_pack') === 'dockerfile' || data_get($this,'health_check_enabled') === false) {
|
||||||
|
ray('dockerfile');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ServiceDatabase::class);
|
return $this->hasMany(ServiceDatabase::class);
|
||||||
}
|
}
|
||||||
|
public function destination()
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
public function environment()
|
public function environment()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Environment::class);
|
return $this->belongsTo(Environment::class);
|
||||||
@@ -124,9 +128,16 @@ class Service extends BaseModel
|
|||||||
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||||
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
||||||
$services = data_get($yaml, 'services');
|
$services = data_get($yaml, 'services');
|
||||||
$definedNetwork = $this->uuid;
|
|
||||||
|
|
||||||
$generatedServiceFQDNS = collect([]);
|
$generatedServiceFQDNS = collect([]);
|
||||||
|
if (is_null($this->destination)) {
|
||||||
|
$destination = $this->server->destinations()->first();
|
||||||
|
if ($destination) {
|
||||||
|
$this->destination()->associate($destination);
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$definedNetwork = collect([$this->uuid]);
|
||||||
|
|
||||||
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS) {
|
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS) {
|
||||||
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||||
@@ -237,18 +248,24 @@ class Service extends BaseModel
|
|||||||
return $value == $definedNetwork;
|
return $value == $definedNetwork;
|
||||||
});
|
});
|
||||||
if (!$definedNetworkExists) {
|
if (!$definedNetworkExists) {
|
||||||
$topLevelNetworks->put($definedNetwork, [
|
foreach ($definedNetwork as $network) {
|
||||||
'name' => $definedNetwork,
|
$topLevelNetworks->put($network, [
|
||||||
'external' => true
|
'name' => $network,
|
||||||
]);
|
'external' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$networks = $serviceNetworks->toArray();
|
$networks = $serviceNetworks->toArray();
|
||||||
$networks = array_merge($networks, [$definedNetwork]);
|
foreach ($definedNetwork as $key => $network) {
|
||||||
|
$networks = array_merge($networks, [
|
||||||
|
$network
|
||||||
|
]);
|
||||||
|
}
|
||||||
data_set($service, 'networks', $networks);
|
data_set($service, 'networks', $networks);
|
||||||
|
|
||||||
// Collect/create/update volumes
|
// Collect/create/update volumes
|
||||||
if ($serviceVolumes->count() > 0) {
|
if ($serviceVolumes->count() > 0) {
|
||||||
foreach ($serviceVolumes as $volume) {
|
$serviceVolumes = $serviceVolumes->map(function ($volume) use ($savedService, $topLevelVolumes, $isNew) {
|
||||||
$type = null;
|
$type = null;
|
||||||
$source = null;
|
$source = null;
|
||||||
$target = null;
|
$target = null;
|
||||||
@@ -276,10 +293,10 @@ class Service extends BaseModel
|
|||||||
}
|
}
|
||||||
if ($type->value() === 'bind') {
|
if ($type->value() === 'bind') {
|
||||||
if ($source->value() === "/var/run/docker.sock") {
|
if ($source->value() === "/var/run/docker.sock") {
|
||||||
continue;
|
return $volume;
|
||||||
}
|
}
|
||||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
continue;
|
return $volume;
|
||||||
}
|
}
|
||||||
LocalFileVolume::updateOrCreate(
|
LocalFileVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
@@ -297,7 +314,21 @@ class Service extends BaseModel
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else if ($type->value() === 'volume') {
|
} else if ($type->value() === 'volume') {
|
||||||
$topLevelVolumes->put($source->value(), null);
|
$slug = Str::slug($source, '-');
|
||||||
|
if ($isNew) {
|
||||||
|
$name = "{$savedService->service->uuid}-{$slug}";
|
||||||
|
} else {
|
||||||
|
$name = "{$savedService->service->uuid}_{$slug}";
|
||||||
|
}
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = Str::of($volume)->before(':');
|
||||||
|
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||||
|
$source = $name;
|
||||||
|
$volume = "$source:$target";
|
||||||
|
} else if (is_array($volume)) {
|
||||||
|
data_set($volume, 'source', $name);
|
||||||
|
}
|
||||||
|
$topLevelVolumes->put($name, null);
|
||||||
LocalPersistentVolume::updateOrCreate(
|
LocalPersistentVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
@@ -305,7 +336,7 @@ class Service extends BaseModel
|
|||||||
'resource_type' => get_class($savedService)
|
'resource_type' => get_class($savedService)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => Str::slug($source, '-'),
|
'name' => $name,
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $savedService->id,
|
||||||
'resource_type' => get_class($savedService)
|
'resource_type' => get_class($savedService)
|
||||||
@@ -313,7 +344,9 @@ class Service extends BaseModel
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
$savedService->getFilesFromServer();
|
$savedService->getFilesFromServer();
|
||||||
}
|
return $volume;
|
||||||
|
});
|
||||||
|
data_set($service, 'volumes', $serviceVolumes->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add env_file with at least .env to the service
|
// Add env_file with at least .env to the service
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.55',
|
'release' => '4.0.0-beta.58',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.55';
|
return '4.0.0-beta.58';
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->boolean('health_check_enabled')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('health_check_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->nullableMorphs('destination');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->dropMorphs('destination');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -7,6 +7,10 @@
|
|||||||
href="{{ route('project.application.deployments', $parameters) }}">
|
href="{{ route('project.application.deployments', $parameters) }}">
|
||||||
<button>Deployments</button>
|
<button>Deployments</button>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('project.application.logs') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('project.application.logs', $parameters) }}">
|
||||||
|
<button>Logs</button>
|
||||||
|
</a>
|
||||||
<x-applications.links :application="$application" />
|
<x-applications.links :application="$application" />
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<x-applications.advanced :application="$application" />
|
<x-applications.advanced :application="$application" />
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
href="{{ route('project.database.configuration', $parameters) }}">
|
href="{{ route('project.database.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('project.database.logs', $parameters) }}">
|
||||||
|
<button>Logs</button>
|
||||||
|
</a>
|
||||||
<a class="{{ request()->routeIs('project.database.backups.all') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.backups.all') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.database.backups.all', $parameters) }}">
|
href="{{ route('project.database.backups.all', $parameters) }}">
|
||||||
<button>Backups</button>
|
<button>Backups</button>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
|
<a class="{{ request()->routeIs('project.service') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('project.service', $parameters) }}">
|
||||||
|
<button>Configuration</button>
|
||||||
|
</a>
|
||||||
<x-services.links :service="$service" />
|
<x-services.links :service="$service" />
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
@if (serviceStatus($service) === 'degraded')
|
@if (serviceStatus($service) === 'degraded')
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<x-boarding-step title="Server">
|
<x-boarding-step title="Server">
|
||||||
<x-slot:question>
|
<x-slot:question>
|
||||||
Do you want to deploy your resources on your <x-highlighted text="Localhost" />
|
Do you want to deploy your resources on your <x-highlighted text="Localhost" />
|
||||||
or on a<x-highlighted text="Remote Server" />?
|
or on a <x-highlighted text="Remote Server" />?
|
||||||
</x-slot:question>
|
</x-slot:question>
|
||||||
<x-slot:actions>
|
<x-slot:actions>
|
||||||
<x-forms.button class="justify-center box" wire:target="setServerType('localhost')"
|
<x-forms.button class="justify-center box" wire:target="setServerType('localhost')"
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
<div>Configuration</div>
|
<div>Configuration</div>
|
||||||
</div>
|
</div>
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<x-forms.button class="w-64" onclick="composeModal.showModal()">Edit Compose File</x-forms.button>
|
<x-forms.button class="w-64" onclick="composeModal.showModal()">Edit Compose
|
||||||
|
File</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input id="service.name" required label="Service Name"
|
<x-forms.input id="service.name" required label="Service Name"
|
||||||
@@ -34,57 +35,66 @@
|
|||||||
</form>
|
</form>
|
||||||
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-3">
|
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-3">
|
||||||
@foreach ($service->applications as $application)
|
@foreach ($service->applications as $application)
|
||||||
<a @class([
|
<div @class([
|
||||||
'border-l border-dashed border-red-500' => Str::of(
|
'border-l border-dashed border-red-500' => Str::of(
|
||||||
$application->status)->contains(['exited']),
|
$application->status)->contains(['exited']),
|
||||||
'border-l border-dashed border-success' => Str::of(
|
'border-l border-dashed border-success' => Str::of(
|
||||||
$application->status)->contains(['running']),
|
$application->status)->contains(['running']),
|
||||||
'border-l border-dashed border-warning' => Str::of(
|
'border-l border-dashed border-warning' => Str::of(
|
||||||
$application->status)->contains(['starting']),
|
$application->status)->contains(['starting']),
|
||||||
'flex flex-col justify-center box',
|
'flex gap-2 box group',
|
||||||
])
|
])>
|
||||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
<a class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||||
@if ($application->human_name)
|
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
||||||
{{ Str::headline($application->human_name) }}
|
|
||||||
@else
|
@if ($application->human_name)
|
||||||
{{ Str::headline($application->name) }}
|
{{ Str::headline($application->human_name) }}
|
||||||
@endif
|
@else
|
||||||
@if ($application->configuration_required)
|
{{ Str::headline($application->name) }}
|
||||||
<span class="text-xs text-error">(configuration required)</span>
|
@endif
|
||||||
@endif
|
@if ($application->configuration_required)
|
||||||
@if ($application->description)
|
<span class="text-xs text-error">(configuration required)</span>
|
||||||
<span class="text-xs">{{ Str::limit($application->description, 60) }}</span>
|
@endif
|
||||||
@endif
|
@if ($application->description)
|
||||||
@if ($application->fqdn)
|
<span class="text-xs">{{ Str::limit($application->description, 60) }}</span>
|
||||||
<span class="text-xs">{{ Str::limit($application->fqdn, 60) }}</span>
|
@endif
|
||||||
@endif
|
@if ($application->fqdn)
|
||||||
<div class="text-xs">{{ $application->status }}</div>
|
<span class="text-xs">{{ Str::limit($application->fqdn, 60) }}</span>
|
||||||
</a>
|
@endif
|
||||||
|
<div class="text-xs">{{ $application->status }}</div>
|
||||||
|
</a>
|
||||||
|
<a class="flex gap-2 p-1 mx-4 text-xs font-bold rounded hover:no-underline hover:text-warning"
|
||||||
|
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $application->name]) }}">Logs</a>
|
||||||
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
@foreach ($databases as $database)
|
@foreach ($databases as $database)
|
||||||
<a @class([
|
<div @class([
|
||||||
'border-l border-dashed border-red-500' => Str::of(
|
'border-l border-dashed border-red-500' => Str::of(
|
||||||
$database->status)->contains(['exited']),
|
$database->status)->contains(['exited']),
|
||||||
'border-l border-dashed border-success' => Str::of(
|
'border-l border-dashed border-success' => Str::of(
|
||||||
$database->status)->contains(['running']),
|
$database->status)->contains(['running']),
|
||||||
'border-l border-dashed border-warning' => Str::of(
|
'border-l border-dashed border-warning' => Str::of(
|
||||||
$database->status)->contains(['restarting']),
|
$database->status)->contains(['restarting']),
|
||||||
'flex flex-col justify-center box',
|
'flex gap-2 box group',
|
||||||
])
|
])>
|
||||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
<a class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||||
@if ($database->human_name)
|
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
||||||
{{ Str::headline($database->human_name) }}
|
@if ($database->human_name)
|
||||||
@else
|
{{ Str::headline($database->human_name) }}
|
||||||
{{ Str::headline($database->name) }}
|
@else
|
||||||
@endif
|
{{ Str::headline($database->name) }}
|
||||||
@if ($database->configuration_required)
|
@endif
|
||||||
<span class="text-xs text-error">(configuration required)</span>
|
@if ($database->configuration_required)
|
||||||
@endif
|
<span class="text-xs text-error">(configuration required)</span>
|
||||||
@if ($database->description)
|
@endif
|
||||||
<span class="text-xs">{{ Str::limit($database->description, 60) }}</span>
|
@if ($database->description)
|
||||||
@endif
|
<span class="text-xs">{{ Str::limit($database->description, 60) }}</span>
|
||||||
<div class="text-xs">{{ $database->status }}</div>
|
@endif
|
||||||
</a>
|
<div class="text-xs">{{ $database->status }}</div>
|
||||||
|
</a>
|
||||||
|
<a class="flex gap-2 p-1 mx-4 text-xs font-bold rounded hover:no-underline hover:text-warning"
|
||||||
|
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $database->name]) }}">Logs</a>
|
||||||
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,12 @@
|
|||||||
<a :class="activeTab === 'storages' && 'text-white'"
|
<a :class="activeTab === 'storages' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
||||||
</a>
|
</a>
|
||||||
|
@if (data_get($parameters, 'service_name'))
|
||||||
|
<a class="{{ request()->routeIs('project.service.logs') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('project.service.logs', $parameters) }}">
|
||||||
|
<button>Logs</button>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full pl-8">
|
<div class="w-full pl-8">
|
||||||
@isset($serviceApplication)
|
@isset($serviceApplication)
|
||||||
@@ -32,7 +38,6 @@
|
|||||||
@isset($serviceDatabase)
|
@isset($serviceDatabase)
|
||||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||||
<livewire:project.service.database :database="$serviceDatabase" />
|
<livewire:project.service.database :database="$serviceDatabase" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div x-cloak x-show="activeTab === 'storages'">
|
<div x-cloak x-show="activeTab === 'storages'">
|
||||||
<livewire:project.shared.storages.all :resource="$serviceDatabase" />
|
<livewire:project.shared.storages.all :resource="$serviceDatabase" />
|
||||||
|
|||||||
22
resources/views/livewire/project/shared/get-logs.blade.php
Normal file
22
resources/views/livewire/project/shared/get-logs.blade.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<div x-init="$wire.getLogs">
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<h2>Logs</h2>
|
||||||
|
@if ($streamLogs)
|
||||||
|
<span wire:poll.2000ms='getLogs(true)' class="loading loading-xs text-warning loading-spinner"></span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<form wire:submit.prevent='getLogs(true)' class="flex items-end gap-2">
|
||||||
|
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required id="numberOfLines"></x-forms.input>
|
||||||
|
<x-forms.button type="submit">Refresh</x-forms.button>
|
||||||
|
</form>
|
||||||
|
<div class="w-32">
|
||||||
|
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="container w-full pt-4 mx-auto">
|
||||||
|
<div
|
||||||
|
class="scrollbar flex flex-col-reverse w-full overflow-y-auto border border-solid rounded border-coolgray-300 max-h-[32rem] p-4 pt-6 text-xs text-white">
|
||||||
|
|
||||||
|
<pre class="font-mono whitespace-pre-wrap">{{ $outputs }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -5,24 +5,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pb-4">Define how your resource's health should be checked.</div>
|
<div class="pb-4">Define how your resource's health should be checked.</div>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex gap-2">
|
<div class="w-32">
|
||||||
<x-forms.input id="resource.health_check_method" placeholder="GET" label="Method" required />
|
<x-forms.checkbox instantSave id="resource.health_check_enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<x-forms.input id="resource.health_check_method" placeholder="GET" label="Method" required />
|
||||||
|
|
||||||
<x-forms.input id="resource.health_check_scheme" placeholder="http" label="Scheme" required />
|
<x-forms.input id="resource.health_check_scheme" placeholder="http" label="Scheme" required />
|
||||||
<x-forms.input id="resource.health_check_host" placeholder="localhost" label="Host" required />
|
<x-forms.input id="resource.health_check_host" placeholder="localhost" label="Host" required />
|
||||||
<x-forms.input id="resource.health_check_port"
|
<x-forms.input id="resource.health_check_port"
|
||||||
helper="If no port is defined, the first exposed port will be used." placeholder="80" label="Port" />
|
helper="If no port is defined, the first exposed port will be used." placeholder="80" label="Port" />
|
||||||
<x-forms.input id="resource.health_check_path" placeholder="/health" label="Path" required />
|
<x-forms.input id="resource.health_check_path" placeholder="/health" label="Path" required />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<x-forms.input id="resource.health_check_return_code" placeholder="200" label="Return Code" required />
|
||||||
|
<x-forms.input id="resource.health_check_response_text" placeholder="OK" label="Response Text" />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<x-forms.input id="resource.health_check_interval" placeholder="30" label="Interval" required />
|
||||||
|
<x-forms.input id="resource.health_check_timeout" placeholder="30" label="Timeout" required />
|
||||||
|
<x-forms.input id="resource.health_check_retries" placeholder="3" label="Retries" required />
|
||||||
|
<x-forms.input id="resource.health_check_start_period" placeholder="30" label="Start Period" required />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
|
||||||
<x-forms.input id="resource.health_check_return_code" placeholder="200" label="Return Code" required />
|
|
||||||
<x-forms.input id="resource.health_check_response_text" placeholder="OK" label="Response Text" />
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<x-forms.input id="resource.health_check_interval" placeholder="30" label="Interval" required />
|
|
||||||
<x-forms.input id="resource.health_check_timeout" placeholder="30" label="Timeout" required />
|
|
||||||
<x-forms.input id="resource.health_check_retries" placeholder="3" label="Retries" required />
|
|
||||||
<x-forms.input id="resource.health_check_start_period" placeholder="30" label="Start Period" required />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
41
resources/views/livewire/project/shared/logs.blade.php
Normal file
41
resources/views/livewire/project/shared/logs.blade.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<x-layout>
|
||||||
|
@if ($type === 'application')
|
||||||
|
<h1>Logs</h1>
|
||||||
|
<livewire:project.application.heading :application="$resource" />
|
||||||
|
<div class="pt-4">
|
||||||
|
@if (Str::of($status)->startsWith('running'))
|
||||||
|
<livewire:project.shared.get-logs :server="$server" :container="$container" />
|
||||||
|
@else
|
||||||
|
Application is not running.
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@elseif ($type === 'database')
|
||||||
|
<h1>Logs</h1>
|
||||||
|
<livewire:project.database.heading :database="$resource" />
|
||||||
|
<div class="pt-4">
|
||||||
|
@if (Str::of($status)->startsWith('running'))
|
||||||
|
<livewire:project.shared.get-logs :server="$server" :container="$container" />
|
||||||
|
@else
|
||||||
|
Database is not running.
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@elseif ($type === 'service')
|
||||||
|
<livewire:project.service.navbar :service="$resource" :parameters="$parameters" :query="$query" />
|
||||||
|
<div class="flex gap-4 pt-6">
|
||||||
|
<div>
|
||||||
|
<a class="{{ request()->routeIs('project.service.show') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('project.service.show', $parameters) }}">
|
||||||
|
<button><- Back</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 pl-8">
|
||||||
|
@if (serviceStatus($resource) === 'running')
|
||||||
|
<livewire:project.shared.get-logs :server="$server" :container="$container" />
|
||||||
|
@else
|
||||||
|
Service is not running.
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</x-layout>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
volume
|
volume
|
||||||
name, example: <span class='text-helper'>-pr-1</span>" />
|
name, example: <span class='text-helper'>-pr-1</span>" />
|
||||||
<x-forms.button class="btn" onclick="newStorage.showModal()">+ Add</x-forms.button>
|
<x-forms.button class="btn" onclick="newStorage.showModal()">+ Add</x-forms.button>
|
||||||
<livewire:project.shared.storages.add />
|
<livewire:project.shared.storages.add :uuid="$resource->uuid" />
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div>Persistent storage to preserve data between deployments.</div>
|
<div>Persistent storage to preserve data between deployments.</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
@endonce
|
@endonce
|
||||||
<form wire:submit.prevent='submit' class="flex flex-col gap-2 pt-4 xl:items-end xl:flex-row">
|
<form wire:submit.prevent='submit' class="flex flex-col gap-2 pt-4 xl:items-end xl:flex-row">
|
||||||
@if ($isReadOnly)
|
@if ($isReadOnly)
|
||||||
<x-forms.input id="realName" label="Volume Name" required readonly />
|
<x-forms.input id="storage.name" label="Volume Name" required readonly />
|
||||||
<x-forms.input id="storage.host_path" label="Source Path" readonly />
|
<x-forms.input id="storage.host_path" label="Source Path" readonly />
|
||||||
<x-forms.input id="storage.mount_path" label="Destination Path" required readonly />
|
<x-forms.input id="storage.mount_path" label="Destination Path" required readonly />
|
||||||
@else
|
@else
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use App\Http\Livewire\Boarding\Index as BoardingIndex;
|
|||||||
use App\Http\Livewire\Project\Service\Index as ServiceIndex;
|
use App\Http\Livewire\Project\Service\Index as ServiceIndex;
|
||||||
use App\Http\Livewire\Project\Service\Show as ServiceShow;
|
use App\Http\Livewire\Project\Service\Show as ServiceShow;
|
||||||
use App\Http\Livewire\Dashboard;
|
use App\Http\Livewire\Dashboard;
|
||||||
|
use App\Http\Livewire\Project\Shared\Logs;
|
||||||
use App\Http\Livewire\Server\All;
|
use App\Http\Livewire\Server\All;
|
||||||
use App\Http\Livewire\Server\Show;
|
use App\Http\Livewire\Server\Show;
|
||||||
use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
|
use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
|
||||||
@@ -80,14 +81,20 @@ Route::middleware(['auth'])->group(function () {
|
|||||||
[ApplicationController::class, 'deployment']
|
[ApplicationController::class, 'deployment']
|
||||||
)->name('project.application.deployment');
|
)->name('project.application.deployment');
|
||||||
|
|
||||||
|
Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/logs', Logs::class)->name('project.application.logs');
|
||||||
|
|
||||||
// Databases
|
// Databases
|
||||||
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
|
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
|
||||||
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
|
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
|
||||||
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
|
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
|
||||||
|
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/logs', Logs::class)->name('project.database.logs');
|
||||||
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', ServiceIndex::class)->name('project.service');
|
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', ServiceIndex::class)->name('project.service');
|
||||||
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}', ServiceShow::class)->name('project.service.show');
|
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}', ServiceShow::class)->name('project.service.show');
|
||||||
|
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}/logs', Logs::class)->name('project.service.logs');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ Route::post('/payments/stripe/events', function () {
|
|||||||
}
|
}
|
||||||
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
|
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
|
||||||
if ($cancelAtPeriodEnd) {
|
if ($cancelAtPeriodEnd) {
|
||||||
send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
|
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
|
||||||
} else {
|
} else {
|
||||||
send_internal_notification('Subscription resumed for team: ' . $subscription->team->id);
|
send_internal_notification('Subscription resumed for team: ' . $subscription->team->id);
|
||||||
}
|
}
|
||||||
@@ -346,7 +346,7 @@ Route::post('/payments/stripe/events', function () {
|
|||||||
'stripe_invoice_paid' => false,
|
'stripe_invoice_paid' => false,
|
||||||
'stripe_trial_already_ended' => true,
|
'stripe_trial_already_ended' => true,
|
||||||
]);
|
]);
|
||||||
send_internal_notification('Subscription cancelled: ' . $subscription->team->id);
|
// send_internal_notification('Subscription cancelled: ' . $subscription->team->id);
|
||||||
break;
|
break;
|
||||||
case 'customer.subscription.trial_will_end':
|
case 'customer.subscription.trial_will_end':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ function help {
|
|||||||
compgen -A function | cat -n
|
compgen -A function | cat -n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setup:dev {
|
||||||
|
docker exec coolify bash -c "composer install"
|
||||||
|
docker exec coolify bash -c "php artisan key:generate"
|
||||||
|
docker exec coolify bash -c "php artisan migrate:fresh --seed"
|
||||||
|
}
|
||||||
function sync:v3 {
|
function sync:v3 {
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
echo -e "Please provide a version.\n\nExample: run sync:v3 3.12.32"
|
echo -e "Please provide a version.\n\nExample: run sync:v3 3.12.32"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"uptime-kuma": {
|
"uptime-kuma": {
|
||||||
"documentation": "https://github.com/louislam/uptime-kuma",
|
"documentation": "https://github.com/louislam/uptime-kuma",
|
||||||
"slogan": "A free and fancy self-hosted monitoring tool.",
|
"slogan": "A free and fancy self-hosted monitoring tool.",
|
||||||
"compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogbG91aXNsYW0vdXB0aW1lLWt1bWE6MQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROCiAgICB2b2x1bWVzOgogICAgICAtIHVwdGltZS1rdW1hOi9hcHAvZGF0YQ=="
|
"compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogbG91aXNsYW0vdXB0aW1lLWt1bWE6MQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROCiAgICB2b2x1bWVzOgogICAgICAtIHVwdGltZS1rdW1hOi9hcHAvZGF0YQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IFsiQ01ELVNIRUxMIiwgImV4dHJhL2hlYWx0aGNoZWNrIl0KICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQ=="
|
||||||
},
|
},
|
||||||
"appsmith": {
|
"appsmith": {
|
||||||
"documentation": "https://docs.appsmith.com/",
|
"documentation": "https://docs.appsmith.com/",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"version": "3.12.36"
|
"version": "3.12.36"
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.55"
|
"version": "4.0.0-beta.58"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user