puh, fixes
This commit is contained in:
parent
03c9793d11
commit
fabb97330a
@ -12,6 +12,7 @@ public function handle(Service $service)
|
||||
{
|
||||
$service->saveComposeConfigs();
|
||||
$commands[] = "cd " . $service->workdir();
|
||||
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
||||
$commands[] = "echo '####### Pulling images.'";
|
||||
$commands[] = "docker compose pull";
|
||||
|
@ -69,7 +69,12 @@ public function new()
|
||||
if ($type->startsWith('one-click-service-')) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
$oneClickDotEnvs = collect(data_get($services, "$oneClickServiceName.envs", []));
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
$oneClickRequiredFqdn = data_get($services, "$oneClickServiceName.generateFqdn", []);
|
||||
$oneClickRequiredFqdn = collect($oneClickRequiredFqdn);
|
||||
if ($oneClickDotEnvs) {
|
||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$service = Service::create([
|
||||
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||
@ -77,18 +82,37 @@ public function new()
|
||||
'environment_id' => $environment->id,
|
||||
'server_id' => (int) $server_id,
|
||||
]);
|
||||
if ($oneClickDotEnvs->count() > 0) {
|
||||
$oneClickDotEnvs->each(function ($value, $key) use ($service) {
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs && $oneClickDotEnvs->count() > 0) {
|
||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||
$key = Str::before($value, '=');
|
||||
$value = Str::of(Str::after($value, '='));
|
||||
if ($value->contains('SERVICE_USER')) {
|
||||
$value = Str::of(Str::random(10));
|
||||
}
|
||||
if ($value->contains('SERVICE_PASSWORD')) {
|
||||
$value = Str::of(Str::password(symbols: false));
|
||||
}
|
||||
if ($value->contains('SERVICE_BASE64')) {
|
||||
$length = Str::of($value)->after('SERVICE_BASE64_')->beforeLast('_')->value();
|
||||
if (is_numeric($length)) {
|
||||
$length = (int) $length;
|
||||
} else {
|
||||
$length = 1;
|
||||
}
|
||||
$value = Str::of(base64_encode(Str::password(length: $length, symbols: false)));
|
||||
}
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'value' => $value->value(),
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
});
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
$service->parse(isNew: true, requiredFqdns: $oneClickRequiredFqdn);
|
||||
|
||||
return redirect()->route('project.service', [
|
||||
'service_uuid' => $service->uuid,
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\New;
|
||||
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Project;
|
||||
use App\Models\Service;
|
||||
use Livewire\Component;
|
||||
@ -11,6 +12,7 @@
|
||||
class DockerCompose extends Component
|
||||
{
|
||||
public string $dockerComposeRaw = '';
|
||||
public string $envFile = '';
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
public function mount()
|
||||
@ -45,13 +47,22 @@ public function submit()
|
||||
|
||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||
|
||||
$service = Service::create([
|
||||
'name' => 'service' . Str::random(10),
|
||||
'docker_compose_raw' => $this->dockerComposeRaw,
|
||||
'environment_id' => $environment->id,
|
||||
'server_id' => (int) $server_id,
|
||||
]);
|
||||
$variables = parseEnvFormatToArray($this->envFile);
|
||||
foreach ($variables as $key => $variable) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $variable,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
'service_id' => $service->id,
|
||||
]);
|
||||
}
|
||||
$service->name = "service-$service->uuid";
|
||||
|
||||
$service->parse(isNew: true);
|
||||
@ -64,6 +75,5 @@ public function submit()
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ class Select extends Component
|
||||
protected $queryString = [
|
||||
'server',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
@ -46,6 +47,7 @@ public function mount()
|
||||
// return handleError($e, $this);
|
||||
// }
|
||||
// }
|
||||
|
||||
public function loadThings()
|
||||
{
|
||||
$this->loadServices();
|
||||
@ -77,7 +79,6 @@ public function loadServices(bool $forceReload = false)
|
||||
});
|
||||
}
|
||||
$this->services = $cached;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
return handleError($e, $this);
|
||||
|
@ -3,26 +3,29 @@
|
||||
namespace App\Http\Livewire\Project\Service;
|
||||
|
||||
use App\Models\ServiceApplication;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Application extends Component
|
||||
{
|
||||
public ServiceApplication $application;
|
||||
public $parameters;
|
||||
public $fileStorages = null;
|
||||
public $fileStorages;
|
||||
protected $listeners = ["refreshFileStorages"];
|
||||
protected $rules = [
|
||||
'application.human_name' => 'nullable',
|
||||
'application.description' => 'nullable',
|
||||
'application.fqdn' => 'nullable',
|
||||
'application.image_tag' => 'required',
|
||||
'application.ignore_from_status' => 'required|boolean',
|
||||
'application.image' => 'required',
|
||||
'application.exclude_from_status' => 'required|boolean',
|
||||
'application.required_fqdn' => 'required|boolean',
|
||||
];
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.application');
|
||||
}
|
||||
public function instantSave() {
|
||||
public function instantSave()
|
||||
{
|
||||
$this->submit();
|
||||
}
|
||||
public function refreshFileStorages()
|
||||
@ -42,6 +45,7 @@ public function delete()
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->fileStorages = collect();
|
||||
$this->refreshFileStorages();
|
||||
}
|
||||
public function submit()
|
||||
@ -49,6 +53,7 @@ public function submit()
|
||||
try {
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
switchImage($this->application);
|
||||
$this->emit('success', 'Application saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
|
@ -8,25 +8,34 @@
|
||||
class Database extends Component
|
||||
{
|
||||
public ServiceDatabase $database;
|
||||
public $fileStorages;
|
||||
protected $listeners = ["refreshFileStorages"];
|
||||
protected $rules = [
|
||||
'database.human_name' => 'nullable',
|
||||
'database.description' => 'nullable',
|
||||
'database.image_tag' => 'required',
|
||||
'database.ignore_from_status' => 'required|boolean',
|
||||
|
||||
'database.image' => 'required',
|
||||
'database.exclude_from_status' => 'required|boolean',
|
||||
];
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.database');
|
||||
}
|
||||
public function mount() {
|
||||
$this->refreshFileStorages();
|
||||
}
|
||||
public function instantSave() {
|
||||
$this->submit();
|
||||
}
|
||||
public function refreshFileStorages()
|
||||
{
|
||||
$this->fileStorages = $this->database->fileStorages()->get();
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
switchImage($this->database);
|
||||
$this->emit('success', 'Database saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
|
@ -15,6 +15,7 @@ class FileStorage extends Component
|
||||
public string $fs_path;
|
||||
|
||||
protected $rules = [
|
||||
'fileStorage.is_directory' => 'required',
|
||||
'fileStorage.fs_path' => 'required',
|
||||
'fileStorage.mount_path' => 'required',
|
||||
'fileStorage.content' => 'nullable',
|
||||
@ -39,6 +40,9 @@ public function submit()
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave() {
|
||||
$this->submit();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.file-storage');
|
||||
|
@ -3,32 +3,57 @@
|
||||
namespace App\Http\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
use WithRateLimiting;
|
||||
public Service $service;
|
||||
public $applications;
|
||||
public $databases;
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
|
||||
protected $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
'service.name' => 'required',
|
||||
'service.description' => 'nullable',
|
||||
];
|
||||
|
||||
public function manualRefreshStack() {
|
||||
try {
|
||||
$this->rateLimit(5);
|
||||
$this->refreshStack();
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refreshStack()
|
||||
{
|
||||
$this->applications = $this->service->applications->sort();
|
||||
$this->applications->each(function ($application) {
|
||||
$application->fileStorages()->get()->each(function ($fileStorage) use ($application) {
|
||||
if (!$fileStorage->is_directory && $fileStorage->content == null) {
|
||||
$application->hasMissingFiles = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
$this->databases = $this->service->databases->sort();
|
||||
$this->databases->each(function ($database) {
|
||||
$database->fileStorages()->get()->each(function ($fileStorage) use ($database) {
|
||||
if (!$fileStorage->is_directory && $fileStorage->content == null) {
|
||||
$database->hasMissingFiles = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
$this->emit('success', 'Stack refreshed successfully.');
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->query = request()->query();
|
||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||
$this->applications = $this->service->applications->sort();
|
||||
$this->databases = $this->service->databases->sort();
|
||||
|
||||
$this->refreshStack();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
@ -43,7 +68,7 @@ public function save()
|
||||
$this->emit('refreshEnvs');
|
||||
$this->emit('success', 'Service saved successfully.');
|
||||
$this->service->saveComposeConfigs();
|
||||
} catch(\Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
@ -39,5 +39,6 @@ public function stop()
|
||||
{
|
||||
StopService::run($this->service);
|
||||
$this->service->refresh();
|
||||
$this->emit('success', 'Service stopped successfully.');
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ public function mount()
|
||||
|
||||
public function delete()
|
||||
{
|
||||
// Should be queued
|
||||
try {
|
||||
if ($this->resource->type() === 'service') {
|
||||
$server = $this->resource->server;
|
||||
|
@ -9,13 +9,13 @@ class Add extends Component
|
||||
public $parameters;
|
||||
public bool $is_preview = false;
|
||||
public string $key;
|
||||
public string $value;
|
||||
public ?string $value = null;
|
||||
public bool $is_build_time = false;
|
||||
|
||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||
protected $rules = [
|
||||
'key' => 'required|string',
|
||||
'value' => 'required|string',
|
||||
'value' => 'nullable',
|
||||
'is_build_time' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
@ -32,6 +32,7 @@ public function mount()
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
ray($this->key, $this->value, $this->is_build_time);
|
||||
$this->emitUp('submit', [
|
||||
'key' => $this->key,
|
||||
'value' => $this->value,
|
||||
|
@ -53,11 +53,11 @@ public function saveVariables($isPreview)
|
||||
$this->resource->environment_variables_preview()->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
ray($variables);
|
||||
$existingVariables = $this->resource->environment_variables();
|
||||
$this->resource->environment_variables()->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
ray($key, $variable);
|
||||
$found = $existingVariables->where('key', $key)->first();
|
||||
if ($found) {
|
||||
$found->value = $variable;
|
||||
@ -110,11 +110,16 @@ public function submit($data)
|
||||
$environment->is_build_time = $data['is_build_time'];
|
||||
$environment->is_preview = $data['is_preview'];
|
||||
|
||||
if ($this->resource->type() === 'application') {
|
||||
$environment->application_id = $this->resource->id;
|
||||
}
|
||||
if ($this->resource->type() === 'standalone-postgresql') {
|
||||
$environment->standalone_postgresql_id = $this->resource->id;
|
||||
switch ($this->resource->type()) {
|
||||
case 'application':
|
||||
$environment->application_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-postgresql':
|
||||
$environment->standalone_postgresql_id = $this->resource->id;
|
||||
break;
|
||||
case 'service':
|
||||
$environment->service_id = $this->resource->id;
|
||||
break;
|
||||
}
|
||||
$environment->save();
|
||||
$this->refreshEnvs();
|
||||
|
@ -15,7 +15,7 @@ class Show extends Component
|
||||
|
||||
protected $rules = [
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
|
@ -151,6 +151,9 @@ public function handle(): void
|
||||
$subType = data_get($labels, 'coolify.service.subType');
|
||||
$subId = data_get($labels, 'coolify.service.subId');
|
||||
$service = $services->where('id', $serviceLabelId)->first();
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
if ($subType === 'application') {
|
||||
$service = $service->applications()->where('id', $subId)->first();
|
||||
} else {
|
||||
|
@ -96,21 +96,47 @@ public function saveComposeConfigs()
|
||||
foreach ($envs as $env) {
|
||||
$commands[] = "echo '{$env->key}={$env->value}' >> .env";
|
||||
}
|
||||
if ($envs->count() === 0) {
|
||||
$commands[] = "touch .env";
|
||||
}
|
||||
instant_remote_process($commands, $this->server);
|
||||
}
|
||||
private function generateFqdn($serviceVariables, $serviceName)
|
||||
private function sslip(Server $server)
|
||||
{
|
||||
if (isDev()) {
|
||||
return "127.0.0.1.sslip.io";
|
||||
}
|
||||
return "{$server->ip}.sslip.io";
|
||||
}
|
||||
private function generateFqdn($serviceVariables, $serviceName, Collection $requiredFqdns)
|
||||
{
|
||||
// Add sslip.io to the service
|
||||
// if (Str::of($serviceVariables)->contains('SERVICE_FQDN') || Str::of($serviceVariables)->contains('SERVICE_URL')) {
|
||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.{$this->server->ip}.sslip.io";
|
||||
if (isDev()) {
|
||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.127.0.0.1.sslip.io";
|
||||
$defaultUsableFqdn = null;
|
||||
$sslip = $this->sslip($this->server);
|
||||
if (Str::of($serviceVariables)->contains('SERVICE_FQDN') || Str::of($serviceVariables)->contains('SERVICE_URL')) {
|
||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.{$sslip}";
|
||||
}
|
||||
if ($requiredFqdns->count() > 0) {
|
||||
foreach ($requiredFqdns as $requiredFqdn) {
|
||||
$requiredFqdn = (array)$requiredFqdn;
|
||||
$name = data_get($requiredFqdn, 'name');
|
||||
$path = data_get($requiredFqdn, 'path');
|
||||
$customFqdn = data_get($requiredFqdn, 'customFqdn');
|
||||
if ($serviceName === $name) {
|
||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.{$sslip}{$path}";
|
||||
if ($customFqdn) {
|
||||
$defaultUsableFqdn = "http://$customFqdn-{$this->uuid}.{$sslip}{$path}";
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
return $defaultUsableFqdn ?? null;
|
||||
}
|
||||
public function parse(bool $isNew = false): Collection
|
||||
public function parse(bool $isNew = false, ?Collection $requiredFqdns = null): Collection
|
||||
{
|
||||
if (!$requiredFqdns) {
|
||||
$requiredFqdns = collect([]);
|
||||
}
|
||||
ray('parsing');
|
||||
// ray()->clearAll();
|
||||
if ($this->docker_compose_raw) {
|
||||
@ -130,16 +156,26 @@ public function parse(bool $isNew = false): Collection
|
||||
$envs = collect([]);
|
||||
$ports = collect([]);
|
||||
|
||||
$services = collect($services)->map(function ($service, $serviceName) use ($composeVolumes, $composeNetworks, $definedNetwork, $envs, $volumes, $ports, $isNew) {
|
||||
$services = collect($services)->map(function ($service, $serviceName) use ($composeVolumes, $composeNetworks, $definedNetwork, $envs, $volumes, $ports, $isNew, $requiredFqdns) {
|
||||
$container_name = "$serviceName-{$this->uuid}";
|
||||
$isDatabase = false;
|
||||
$serviceVariables = collect(data_get($service, 'environment', []));
|
||||
|
||||
// Add env_file with at least .env to the service
|
||||
$envFile = collect(data_get($service, 'env_file', []));
|
||||
if ($envFile->count() > 0) {
|
||||
if (!$envFile->contains('.env')) {
|
||||
$envFile->push('.env');
|
||||
}
|
||||
} else {
|
||||
$envFile = collect(['.env']);
|
||||
}
|
||||
data_set($service, 'env_file', $envFile->toArray());
|
||||
|
||||
// Decide if the service is a database
|
||||
$image = data_get($service, 'image');
|
||||
if ($image) {
|
||||
$imageName = Str::of($image)->before(':');
|
||||
$imageTag = Str::of($image)->after(':') ?? 'latest';
|
||||
if (collect(DATABASE_DOCKER_IMAGES)->contains($imageName)) {
|
||||
$isDatabase = true;
|
||||
data_set($service, 'is_database', true);
|
||||
@ -160,17 +196,29 @@ public function parse(bool $isNew = false): Collection
|
||||
if ($isDatabase) {
|
||||
$savedService = ServiceDatabase::create([
|
||||
'name' => $serviceName,
|
||||
'image_tag' => $imageTag,
|
||||
'image' => $image,
|
||||
'service_id' => $this->id
|
||||
]);
|
||||
} else {
|
||||
$savedService = ServiceApplication::create([
|
||||
'name' => $serviceName,
|
||||
'fqdn' => $this->generateFqdn($serviceVariables, $serviceName),
|
||||
'image_tag' => $imageTag,
|
||||
'fqdn' => $this->generateFqdn($serviceVariables, $serviceName, $requiredFqdns),
|
||||
'image' => $image,
|
||||
'service_id' => $this->id
|
||||
]);
|
||||
}
|
||||
if ($requiredFqdns->count() > 0) {
|
||||
$found = false;
|
||||
foreach ($requiredFqdns as $requiredFqdn) {
|
||||
$requiredFqdn = (array)$requiredFqdn;
|
||||
$name = data_get($requiredFqdn, 'name');
|
||||
if ($serviceName === $name) {
|
||||
$savedService->required_fqdn = true;
|
||||
$savedService->save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($isDatabase) {
|
||||
$savedService = $this->databases()->whereName($serviceName)->first();
|
||||
@ -179,14 +227,13 @@ public function parse(bool $isNew = false): Collection
|
||||
if (data_get($savedService, 'fqdn')) {
|
||||
$defaultUsableFqdn = data_get($savedService, 'fqdn', null);
|
||||
} else {
|
||||
$defaultUsableFqdn = $this->generateFqdn($serviceVariables, $serviceName);
|
||||
$defaultUsableFqdn = $this->generateFqdn($serviceVariables, $serviceName, $requiredFqdns);
|
||||
}
|
||||
$savedService->fqdn = $defaultUsableFqdn;
|
||||
$savedService->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Set image tag
|
||||
$fqdns = data_get($savedService, 'fqdn');
|
||||
if ($fqdns) {
|
||||
$fqdns = collect(Str::of($fqdns)->explode(','));
|
||||
@ -209,6 +256,7 @@ public function parse(bool $isNew = false): Collection
|
||||
}
|
||||
$savedService->ports = $collectedPorts->implode(',');
|
||||
$savedService->save();
|
||||
|
||||
// Collect volumes
|
||||
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||
if ($serviceVolumes->count() > 0) {
|
||||
@ -341,6 +389,7 @@ public function parse(bool $isNew = false): Collection
|
||||
$value = Str::after($variable, '=');
|
||||
if (!Str::startsWith($value, '$SERVICE_') && !Str::startsWith($value, '${SERVICE_') && Str::startsWith($value, '$')) {
|
||||
$value = Str::of(replaceVariables(Str::of($value)));
|
||||
$nakedName = $nakedValue = null;
|
||||
if ($value->contains(':')) {
|
||||
$nakedName = $value->before(':');
|
||||
$nakedValue = $value->after(':');
|
||||
@ -464,6 +513,7 @@ public function parse(bool $isNew = false): Collection
|
||||
$number = 0;
|
||||
}
|
||||
$fqdn = getFqdnWithoutPort(data_get($fqdns, $number, $fqdns->first()));
|
||||
ray($fqdns);
|
||||
$environments = collect(data_get($service, 'environment'));
|
||||
$environments = $environments->map(function ($envValue) use ($value, $fqdn) {
|
||||
$envValue = Str::of($envValue)->replace($value, $fqdn);
|
||||
@ -491,6 +541,7 @@ public function parse(bool $isNew = false): Collection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add labels to the service
|
||||
$labels = collect(data_get($service, 'labels', []));
|
||||
$labels = collect([]);
|
||||
|
@ -18,7 +18,6 @@ public function documentation()
|
||||
{
|
||||
$services = Cache::get('services', []);
|
||||
$service = data_get($services, $this->name, []);
|
||||
ray($this->name);
|
||||
return data_get($service, 'documentation', 'https://coolify.io/docs');
|
||||
}
|
||||
public function service()
|
||||
|
@ -3,7 +3,9 @@
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
function replaceRegex(?string $name = null)
|
||||
{
|
||||
@ -22,33 +24,41 @@ function serviceStatus(Service $service)
|
||||
{
|
||||
$foundRunning = false;
|
||||
$isDegraded = false;
|
||||
$foundRestaring = false;
|
||||
$applications = $service->applications;
|
||||
$databases = $service->databases;
|
||||
foreach ($applications as $application) {
|
||||
if ($application->ignore_from_status) {
|
||||
if ($application->exclude_from_status) {
|
||||
continue;
|
||||
}
|
||||
if (Str::of($application->status)->startsWith('running')) {
|
||||
$foundRunning = true;
|
||||
} else if (Str::of($application->status)->startsWith('restarting')) {
|
||||
$foundRestaring = true;
|
||||
} else {
|
||||
$isDegraded = true;
|
||||
}
|
||||
}
|
||||
foreach ($databases as $database) {
|
||||
if ($database->ignore_from_status) {
|
||||
if ($database->exclude_from_status) {
|
||||
continue;
|
||||
}
|
||||
if (Str::of($database->status)->startsWith('running')) {
|
||||
$foundRunning = true;
|
||||
} else if (Str::of($database->status)->startsWith('restarting')) {
|
||||
$foundRestaring = true;
|
||||
} else {
|
||||
$isDegraded = true;
|
||||
}
|
||||
}
|
||||
if ($foundRestaring) {
|
||||
return 'degraded';
|
||||
}
|
||||
if ($foundRunning && !$isDegraded) {
|
||||
return 'running';
|
||||
} else if ($foundRunning && $isDegraded) {
|
||||
return 'degraded';
|
||||
} else if (!$foundRunning && $isDegraded) {
|
||||
} else if (!$foundRunning && !$isDegraded) {
|
||||
return 'exited';
|
||||
}
|
||||
return 'exited';
|
||||
@ -60,19 +70,25 @@ function saveFileVolumesHelper(ServiceApplication|ServiceDatabase $oneService)
|
||||
$server = $oneService->service->server;
|
||||
$applicationFileVolume = $oneService->fileStorages()->get();
|
||||
$commands = collect([
|
||||
"mkdir -p $workdir",
|
||||
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||
"cd $workdir"
|
||||
]);
|
||||
foreach ($applicationFileVolume as $fileVolume) {
|
||||
$content = $fileVolume->content;
|
||||
$path = Str::of($fileVolume->fs_path);
|
||||
if ($fileVolume->is_directory) {
|
||||
$commands->push("test -f $path && rm -f $path > /dev/null 2>&1 || true");
|
||||
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||
continue;
|
||||
}
|
||||
$content = $fileVolume->content;
|
||||
$dir = $path->beforeLast('/');
|
||||
if ($dir->startsWith('.')) {
|
||||
$dir = $dir->after('.');
|
||||
$dir = $workdir . $dir;
|
||||
}
|
||||
$content = base64_encode($content);
|
||||
$commands->push("mkdir -p $dir");
|
||||
$commands->push("test -d $path && rm -rf $path > /dev/null 2>&1 || true");
|
||||
$commands->push("mkdir -p $dir > /dev/null 2>&1 || true");
|
||||
$commands->push("echo '$content' | base64 -d > $path");
|
||||
}
|
||||
return instant_remote_process($commands, $server);
|
||||
@ -80,3 +96,18 @@ function saveFileVolumesHelper(ServiceApplication|ServiceDatabase $oneService)
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
function switchImage($resource)
|
||||
{
|
||||
try {
|
||||
$name = data_get($resource, 'name');
|
||||
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
|
||||
$image = data_get($resource, 'image');
|
||||
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
||||
data_set($dockerCompose, "services.{$name}.image", $image);
|
||||
$dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
|
||||
$resource->service->docker_compose_raw = $dockerComposeRaw;
|
||||
$resource->service->save();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,9 @@
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('service_applications', function (Blueprint $table) {
|
||||
$table->boolean('ignore_from_status')->default(false);
|
||||
$table->string('image_tag')->nullable();
|
||||
$table->boolean('exclude_from_status')->default(false);
|
||||
$table->boolean('required_fqdn')->default(false);
|
||||
$table->string('image')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
@ -23,8 +24,9 @@ public function up(): void
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('service_applications', function (Blueprint $table) {
|
||||
$table->dropColumn('ignore_from_status');
|
||||
$table->dropColumn('image_tag');
|
||||
$table->dropColumn('exclude_from_status');
|
||||
$table->dropColumn('required_fqdn');
|
||||
$table->dropColumn('image');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -12,8 +12,8 @@
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('service_databases', function (Blueprint $table) {
|
||||
$table->boolean('ignore_from_status')->default(false);
|
||||
$table->string('image_tag')->nullable();
|
||||
$table->boolean('exclude_from_status')->default(false);
|
||||
$table->string('image')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
@ -23,8 +23,8 @@ public function up(): void
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('service_databases', function (Blueprint $table) {
|
||||
$table->dropColumn('ignore_from_status');
|
||||
$table->dropColumn('image_tag');
|
||||
$table->dropColumn('exclude_from_status');
|
||||
$table->dropColumn('image');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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('local_file_volumes', function (Blueprint $table) {
|
||||
$table->boolean('is_directory')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('local_file_volumes', function (Blueprint $table) {
|
||||
$table->dropColumn('is_directory');
|
||||
});
|
||||
}
|
||||
};
|
File diff suppressed because one or more lines are too long
@ -11,12 +11,13 @@
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li title="Send feedback or get help" class="fixed top-0 right-0 p-2 px-4 pt-2 mt-auto">
|
||||
<div class="justify-center text-xs text-warning" wire:click="help" onclick="help.showModal()">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" d="M22 5.5H9c-1.1 0-2 .9-2 2v9a2 2 0 0 0 2 2h13c1.11 0 2-.89 2-2v-9a2 2 0 0 0-2-2m0 11H9V9.17l6.5 3.33L22 9.17v7.33m-6.5-5.69L9 7.5h13l-6.5 3.31M5 16.5c0 .17.03.33.05.5H1c-.552 0-1-.45-1-1s.448-1 1-1h4v1.5M3 7h2.05c-.02.17-.05.33-.05.5V9H3c-.55 0-1-.45-1-1s.45-1 1-1m-2 5c0-.55.45-1 1-1h3v2H2c-.55 0-1-.45-1-1Z"/>
|
||||
<li title="Send feedback or get help" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto">
|
||||
<div class="justify-center" wire:click="help" onclick="help.showModal()">
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="M22 5.5H9c-1.1 0-2 .9-2 2v9a2 2 0 0 0 2 2h13c1.11 0 2-.89 2-2v-9a2 2 0 0 0-2-2m0 11H9V9.17l6.5 3.33L22 9.17v7.33m-6.5-5.69L9 7.5h13l-6.5 3.31M5 16.5c0 .17.03.33.05.5H1c-.552 0-1-.45-1-1s.448-1 1-1h4v1.5M3 7h2.05c-.02.17-.05.33-.05.5V9H3c-.55 0-1-.45-1-1s.45-1 1-1m-2 5c0-.55.45-1 1-1h3v2H2c-.55 0-1-.45-1-1Z" />
|
||||
</svg>
|
||||
Feedback
|
||||
Feedback
|
||||
</div>
|
||||
</li>
|
||||
<li class="pb-6" title="Logout">
|
||||
|
@ -116,8 +116,8 @@ class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox=
|
||||
@endif
|
||||
@if (isSubscriptionActive() || isDev())
|
||||
<li title="Send feedback or get help" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto">
|
||||
<div class="justify-center text-xs text-warning" wire:click="help" onclick="help.showModal()">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<div class="justify-center" wire:click="help" onclick="help.showModal()">
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="M22 5.5H9c-1.1 0-2 .9-2 2v9a2 2 0 0 0 2 2h13c1.11 0 2-.89 2-2v-9a2 2 0 0 0-2-2m0 11H9V9.17l6.5 3.33L22 9.17v7.33m-6.5-5.69L9 7.5h13l-6.5 3.31M5 16.5c0 .17.03.33.05.5H1c-.552 0-1-.45-1-1s.448-1 1-1h4v1.5M3 7h2.05c-.02.17-.05.33-.05.5V9H3c-.55 0-1-.45-1-1s.45-1 1-1m-2 5c0-.55.45-1 1-1h3v2H2c-.55 0-1-.45-1-1Z" />
|
||||
</svg>
|
||||
|
@ -1,27 +1,7 @@
|
||||
<div class="navbar-main">
|
||||
<x-services.links :service="$service" />
|
||||
<div class="flex-1"></div>
|
||||
@if (serviceStatus($service) === 'running')
|
||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
|
||||
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
|
||||
</svg>
|
||||
Stop
|
||||
</button>
|
||||
@elseif(serviceStatus($service) === 'exited')
|
||||
<button wire:click='deploy' onclick="startService.showModal()"
|
||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M7 4v16l13 -8z" />
|
||||
</svg>
|
||||
Deploy
|
||||
</button>
|
||||
@elseif (serviceStatus($service) === 'degraded')
|
||||
@if (serviceStatus($service) === 'degraded')
|
||||
<button wire:click='deploy' onclick="startService.showModal()"
|
||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
@ -42,4 +22,34 @@ class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400"
|
||||
Stop
|
||||
</button>
|
||||
@endif
|
||||
@if (serviceStatus($service) === 'running')
|
||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
|
||||
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path>
|
||||
</svg>
|
||||
Stop
|
||||
</button>
|
||||
@endif
|
||||
@if (serviceStatus($service) === 'exited')
|
||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
|
||||
<path fill="red"
|
||||
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
|
||||
</svg>
|
||||
Force cleanup containers
|
||||
</button>
|
||||
<button wire:click='deploy' onclick="startService.showModal()"
|
||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M7 4v16l13 -8z" />
|
||||
</svg>
|
||||
Deploy
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@
|
||||
</div>
|
||||
@endif
|
||||
<div
|
||||
class="scrollbar flex flex-col-reverse w-full overflow-y-auto border border-solid rounded border-coolgray-300 max-h-[32rem] p-4 text-xs text-white">
|
||||
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" @if ($isPollingActive) wire:poll.2000ms="polling" @endif>{{ RunRemoteProcess::decodeOutput($this->activity) }}</pre>
|
||||
</div>
|
||||
|
@ -44,5 +44,6 @@
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
|
||||
'></x-forms.textarea>
|
||||
<x-forms.textarea label="Environment File" rows="20" id="envFile"></x-forms.textarea>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -17,22 +17,30 @@
|
||||
<x-forms.input label="Description" id="application.description"></x-forms.input>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
||||
id="application.fqdn"></x-forms.input>
|
||||
<x-forms.input required helper="You can change the image tag you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>" label="Image Tag" id="application.image_tag"></x-forms.input>
|
||||
@if ($application->required_fqdn)
|
||||
<x-forms.input required placeholder="https://app.coolify.io" label="Domains"
|
||||
id="application.fqdn"></x-forms.input>
|
||||
@else
|
||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
||||
id="application.fqdn"></x-forms.input>
|
||||
@endif
|
||||
<x-forms.input required
|
||||
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||
label="Image" id="application.image"></x-forms.input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="pt-2">Advanced</h3>
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox instantSave label="Ignore from service status"
|
||||
<x-forms.checkbox instantSave label="Exclude from service status"
|
||||
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
||||
id="application.ignore_from_status"></x-forms.checkbox>
|
||||
id="application.exclude_from_status"></x-forms.checkbox>
|
||||
</div>
|
||||
</form>
|
||||
@if ($fileStorages->count() > 0)
|
||||
<h3 class="py-4">Files</h3>
|
||||
<h3 class="py-4">Mounted Files (binds)</h3>
|
||||
<div class="flex flex-col gap-4">
|
||||
@foreach ($fileStorages->get() as $fileStorage)
|
||||
@foreach ($fileStorages as $fileStorage)
|
||||
<livewire:project.service.file-storage :fileStorage="$fileStorage" wire:key="{{ $loop->index }}" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
@ -15,21 +15,21 @@
|
||||
<x-forms.input label="Description" id="database.description"></x-forms.input>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input required helper="You can change the image tag you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>" label="Image Tag"
|
||||
id="database.image_tag"></x-forms.input>
|
||||
<x-forms.input required helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>" label="Image Tag"
|
||||
id="database.image"></x-forms.input>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="pt-2">Advanced</h3>
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox instantSave label="Ignore from service status"
|
||||
<x-forms.checkbox instantSave label="Exclude from service status"
|
||||
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
||||
id="database.ignore_from_status"></x-forms.checkbox>
|
||||
id="database.exclude_from_status"></x-forms.checkbox>
|
||||
</div>
|
||||
</form>
|
||||
@if ($database->fileStorages()->get()->count() > 0)
|
||||
@if ($fileStorages->count() > 0)
|
||||
<h3 class="py-4">Mounted Files (binds)</h3>
|
||||
<div class="flex flex-col gap-4">
|
||||
@foreach ($database->fileStorages()->get() as $fileStorage)
|
||||
@foreach ($fileStorages as $fileStorage)
|
||||
<livewire:project.service.file-storage :fileStorage="$fileStorage" wire:key="{{ $loop->index }}" />
|
||||
@endforeach
|
||||
</div>
|
||||
|
@ -1,20 +1,26 @@
|
||||
<x-collapsible>
|
||||
<x-slot:title>
|
||||
<div>{{ $fileStorage->mount_path }}
|
||||
@empty($fileStorage->content)
|
||||
@if(is_null($fileStorage->content) && !$fileStorage->is_directory)
|
||||
<span class="text-xs text-warning">(empty)</span>
|
||||
@endempty
|
||||
@endif
|
||||
</div>
|
||||
</x-slot:title>
|
||||
<x-slot:action>
|
||||
<form wire:submit.prevent='submit' class="flex flex-col gap-2">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input readonly label="File in Docker Compose file" id="fileStorage.fs_path"></x-forms.input>
|
||||
<x-forms.input readonly label="File on Filesystem" id="fs_path"></x-forms.input>
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox instantSave label="Is directory?" id="fileStorage.is_directory"></x-forms.checkbox>
|
||||
</div>
|
||||
<x-forms.input readonly label="Mount (in container)" id="fileStorage.mount_path"></x-forms.input>
|
||||
<x-forms.textarea label="Content" rows="20" id="fileStorage.content"></x-forms.textarea>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
<x-forms.input readonly label="Save your required files here:" id="fs_path"></x-forms.input>
|
||||
@if (!$fileStorage->is_directory)
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input readonly label="File in Docker Compose file" id="fileStorage.fs_path"></x-forms.input>
|
||||
<x-forms.input readonly label="File on Filesystem" id="fs_path"></x-forms.input>
|
||||
</div>
|
||||
<x-forms.input readonly label="Mount (in container)" id="fileStorage.mount_path"></x-forms.input>
|
||||
<x-forms.textarea label="Content" rows="20" id="fileStorage.content"></x-forms.textarea>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
@endif
|
||||
</form>
|
||||
</x-slot:action>
|
||||
</x-collapsible>
|
||||
|
@ -28,7 +28,10 @@
|
||||
<x-forms.input id="service.description" label="Description" />
|
||||
</div>
|
||||
</form>
|
||||
<h2 class="pb-4"> Service Stack </h2>
|
||||
<div class="flex gap-2">
|
||||
<h2 class="pb-4"> Service Stack </h2>
|
||||
<x-forms.button wire:click='manualRefreshStack'>Refresh</x-forms.button>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-3 ">
|
||||
@foreach ($applications as $application)
|
||||
<a @class([
|
||||
@ -46,6 +49,9 @@
|
||||
@else
|
||||
{{ Str::headline($application->name) }}
|
||||
@endif
|
||||
@if ($application->hasMissingFiles)
|
||||
<span class="text-xs text-error">(has missing files)</span>
|
||||
@endif
|
||||
@if ($application->description)
|
||||
<span class="text-xs">{{ $application->description }}</span>
|
||||
@endif
|
||||
@ -58,11 +64,11 @@
|
||||
@foreach ($databases as $database)
|
||||
<a @class([
|
||||
'border-l border-dashed border-red-500' => Str::of(
|
||||
$application->status)->contains(['exited']),
|
||||
$database->status)->contains(['exited']),
|
||||
'border-l border-dashed border-success' => Str::of(
|
||||
$application->status)->contains(['running']),
|
||||
$database->status)->contains(['running']),
|
||||
'border-l border-dashed border-warning' => Str::of(
|
||||
$application->status)->contains(['restarting']),
|
||||
$database->status)->contains(['restarting']),
|
||||
'flex flex-col justify-center box',
|
||||
])
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
||||
@ -71,10 +77,13 @@
|
||||
@else
|
||||
{{ Str::headline($database->name) }}
|
||||
@endif
|
||||
@if ($database->hasMissingFiles)
|
||||
<span class="text-xs text-error">(has missing files)</span>
|
||||
@endif
|
||||
@if ($database->description)
|
||||
<span class="text-xs">{{ $database->description }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $application->status }}</div>
|
||||
<div class="text-xs">{{ $database->status }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
@ -28,13 +28,13 @@
|
||||
@endif
|
||||
@else
|
||||
<form wire:submit.prevent='saveVariables(false)' class="flex flex-col gap-2">
|
||||
<x-forms.textarea rows=5 class="whitespace-pre-wrap"
|
||||
<x-forms.textarea rows=25 class="whitespace-pre-wrap"
|
||||
id="variables"></x-forms.textarea>
|
||||
<x-forms.button type="submit" class="btn btn-primary">Save</x-forms.button>
|
||||
</form>
|
||||
@if ($showPreview)
|
||||
<form wire:submit.prevent='saveVariables(true)' class="flex flex-col gap-2">
|
||||
<x-forms.textarea rows=5 class="whitespace-pre-wrap" label="Preview Environment Variables"
|
||||
<x-forms.textarea rows=25 class="whitespace-pre-wrap" label="Preview Environment Variables"
|
||||
id="variablesPreview"></x-forms.textarea>
|
||||
<x-forms.button type="submit" class="btn btn-primary">Save</x-forms.button>
|
||||
</form>
|
||||
|
@ -8,16 +8,7 @@
|
||||
@else
|
||||
<x-status.stopped status="Proxy Stopped" />
|
||||
@endif
|
||||
<button wire:loading.remove.delay.longer wire:click.prevent='getProxyStatusWithNoti'>
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#FCD44F">
|
||||
<path
|
||||
d="M12.079 3v-.75V3Zm-8.4 8.333h-.75h.75Zm0 1.667l-.527.532a.75.75 0 0 0 1.056 0L3.68 13Zm2.209-1.134A.75.75 0 1 0 4.83 10.8l1.057 1.065ZM2.528 10.8a.75.75 0 0 0-1.056 1.065L2.528 10.8Zm16.088-3.408a.75.75 0 1 0 1.277-.786l-1.277.786ZM12.079 2.25c-5.047 0-9.15 4.061-9.15 9.083h1.5c0-4.182 3.42-7.583 7.65-7.583v-1.5Zm-9.15 9.083V13h1.5v-1.667h-1.5Zm1.28 2.2l1.679-1.667L4.83 10.8l-1.68 1.667l1.057 1.064Zm0-1.065L2.528 10.8l-1.057 1.065l1.68 1.666l1.056-1.064Zm15.684-5.86A9.158 9.158 0 0 0 12.08 2.25v1.5a7.658 7.658 0 0 1 6.537 3.643l1.277-.786Z" />
|
||||
<path fill="#fff"
|
||||
d="M11.883 21v.75V21Zm8.43-8.333h.75h-.75Zm0-1.667l.528-.533a.75.75 0 0 0-1.055 0l.528.533ZM18.1 12.133a.75.75 0 1 0 1.055 1.067L18.1 12.133Zm3.373 1.067a.75.75 0 1 0 1.054-1.067L21.473 13.2ZM5.318 16.606a.75.75 0 1 0-1.277.788l1.277-.788Zm6.565 5.144c5.062 0 9.18-4.058 9.18-9.083h-1.5c0 4.18-3.43 7.583-7.68 7.583v1.5Zm9.18-9.083V11h-1.5v1.667h1.5Zm-1.277-2.2L18.1 12.133l1.055 1.067l1.686-1.667l-1.055-1.066Zm0 1.066l1.687 1.667l1.054-1.067l-1.686-1.666l-1.055 1.066Zm-15.745 5.86a9.197 9.197 0 0 0 7.841 4.357v-1.5a7.697 7.697 0 0 1-6.564-3.644l-1.277.788Z"
|
||||
opacity=".5" />
|
||||
</g>
|
||||
</svg></button>
|
||||
<x-forms.button wire:click='getProxyStatusWithNoti'>Refresh </x-forms.button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user