feat: services
This commit is contained in:
parent
58522b59b7
commit
0b11093d18
@ -14,9 +14,9 @@ public function handle(Service $service)
|
||||
$commands[] = "cd " . $service->workdir();
|
||||
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
||||
$commands[] = "echo '####### Pulling images.'";
|
||||
$commands[] = "docker compose pull --quiet";
|
||||
$commands[] = "docker compose pull";
|
||||
$commands[] = "echo '####### Starting containers.'";
|
||||
$commands[] = "docker compose up -d >/dev/null 2>&1";
|
||||
$commands[] = "docker compose up -d";
|
||||
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
||||
$activity = remote_process($commands, $service->server);
|
||||
return $activity;
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
@ -41,9 +44,10 @@ public function show()
|
||||
|
||||
public function new()
|
||||
{
|
||||
$type = request()->query('type');
|
||||
$services = Cache::get('services', []);
|
||||
$type = Str::of(request()->query('type'));
|
||||
$destination_uuid = request()->query('destination');
|
||||
$server = requesT()->query('server');
|
||||
$server_id = request()->query('server');
|
||||
|
||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||
if (!$project) {
|
||||
@ -61,8 +65,28 @@ public function new()
|
||||
'database_uuid' => $standalone_postgresql->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-')) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, $oneClickServiceName);
|
||||
if ($oneClickService) {
|
||||
$service = Service::create([
|
||||
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'server_id' => (int) $server_id,
|
||||
]);
|
||||
|
||||
$service->parse(isNew: true);
|
||||
|
||||
return redirect()->route('project.service', [
|
||||
'service_uuid' => $service->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'project_uuid' => $project->uuid,
|
||||
]);
|
||||
}
|
||||
}
|
||||
return view('project.new', [
|
||||
'type' => $type
|
||||
'type' => $type->value()
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -10,15 +10,16 @@
|
||||
|
||||
class DockerCompose extends Component
|
||||
{
|
||||
public string $dockercompose = '';
|
||||
public string $dockerComposeRaw = '';
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
public function mount()
|
||||
{
|
||||
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->query = request()->query();
|
||||
if (isDev()) {
|
||||
$this->dockercompose = 'services:
|
||||
$this->dockerComposeRaw = 'services:
|
||||
plausible_events_db:
|
||||
image: clickhouse/clickhouse-server:23.3.7.5-alpine
|
||||
restart: always
|
||||
@ -37,9 +38,9 @@ public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'dockercompose' => 'required'
|
||||
'dockerComposeRaw' => 'required'
|
||||
]);
|
||||
$this->dockercompose = Yaml::dump(Yaml::parse($this->dockercompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
||||
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
||||
$server_id = $this->query['server_id'];
|
||||
|
||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||
@ -47,7 +48,7 @@ public function submit()
|
||||
|
||||
$service = Service::create([
|
||||
'name' => 'service' . Str::random(10),
|
||||
'docker_compose_raw' => $this->dockercompose,
|
||||
'docker_compose_raw' => $this->dockerComposeRaw,
|
||||
'environment_id' => $environment->id,
|
||||
'server_id' => (int) $server_id,
|
||||
]);
|
||||
|
@ -3,12 +3,12 @@
|
||||
namespace App\Http\Livewire\Project\New;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Countable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Component;
|
||||
use Route;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Select extends Component
|
||||
{
|
||||
@ -21,6 +21,8 @@ class Select extends Component
|
||||
public Collection|array $standaloneDockers = [];
|
||||
public Collection|array $swarmDockers = [];
|
||||
public array $parameters;
|
||||
public Collection|array $services = [];
|
||||
public bool $loadingServices = true;
|
||||
|
||||
public ?string $existingPostgresqlUrl = null;
|
||||
|
||||
@ -44,6 +46,35 @@ public function mount()
|
||||
// return handleError($e, $this);
|
||||
// }
|
||||
// }
|
||||
public function loadThings()
|
||||
{
|
||||
$this->loadServices();
|
||||
$this->loadServers();
|
||||
}
|
||||
public function loadServices(bool $forceReload = false)
|
||||
{
|
||||
try {
|
||||
if ($forceReload) {
|
||||
Cache::forget('services');
|
||||
}
|
||||
$cached = Cache::remember('services', 3600, function () {
|
||||
$services = Http::get(config('constants.services.offical'));
|
||||
if ($services->failed()) {
|
||||
throw new \Exception($services->body());
|
||||
}
|
||||
|
||||
$services = collect($services->json());
|
||||
$this->emit('success', 'Successfully reloaded services from the internet.');
|
||||
return $services;
|
||||
});
|
||||
$this->services = $cached;
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->loadingServices = false;
|
||||
}
|
||||
}
|
||||
public function setType(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
@ -87,7 +118,7 @@ public function setDestination(string $destination_uuid)
|
||||
]);
|
||||
}
|
||||
|
||||
public function load_servers()
|
||||
public function loadServers()
|
||||
{
|
||||
$this->servers = Server::isUsable()->get();
|
||||
}
|
||||
|
@ -8,23 +8,39 @@
|
||||
class Application extends Component
|
||||
{
|
||||
public ServiceApplication $application;
|
||||
public $parameters;
|
||||
public $fileStorages = null;
|
||||
protected $listeners = ["refreshFileStorages"];
|
||||
protected $rules = [
|
||||
'application.human_name' => 'nullable',
|
||||
'application.description' => 'nullable',
|
||||
'application.fqdn' => 'nullable',
|
||||
'application.ignore_from_status' => 'required|boolean',
|
||||
];
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.application');
|
||||
}
|
||||
public function instantSave() {
|
||||
$this->submit();
|
||||
}
|
||||
public function refreshFileStorages()
|
||||
{
|
||||
$this->fileStorages = $this->application->fileStorages()->get();
|
||||
}
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
$this->application->delete();
|
||||
$this->emit('success', 'Application deleted successfully.');
|
||||
return redirect()->route('project.service', $this->parameters);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->refreshFileStorages();
|
||||
}
|
||||
public function submit()
|
||||
|
@ -12,16 +12,22 @@ class Database extends Component
|
||||
protected $rules = [
|
||||
'database.human_name' => 'nullable',
|
||||
'database.description' => 'nullable',
|
||||
'database.ignore_from_status' => 'required|boolean',
|
||||
|
||||
];
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.database');
|
||||
}
|
||||
public function instantSave() {
|
||||
$this->submit();
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->emit('success', 'Database saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
} finally {
|
||||
|
@ -3,14 +3,17 @@
|
||||
namespace App\Http\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public Service $service;
|
||||
|
||||
public $applications;
|
||||
public $databases;
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
|
||||
protected $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
@ -23,6 +26,9 @@ 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();
|
||||
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
@ -30,12 +36,16 @@ public function render()
|
||||
}
|
||||
public function save()
|
||||
{
|
||||
$this->service->save();
|
||||
$this->service->parse();
|
||||
$this->service->refresh();
|
||||
$this->emit('refreshEnvs');
|
||||
$this->emit('success', 'Service saved successfully.');
|
||||
$this->service->saveComposeConfigs();
|
||||
try {
|
||||
$this->service->save();
|
||||
$this->service->parse();
|
||||
$this->service->refresh();
|
||||
$this->emit('refreshEnvs');
|
||||
$this->emit('success', 'Service saved successfully.');
|
||||
$this->service->saveComposeConfigs();
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
@ -76,7 +76,6 @@ public function handle(): void
|
||||
$databases = $this->server->databases();
|
||||
$services = $this->server->services();
|
||||
$previews = $this->server->previews();
|
||||
|
||||
$this->server->proxyType();
|
||||
/// Check if proxy is running
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
@ -149,19 +148,20 @@ public function handle(): void
|
||||
}
|
||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||
if ($serviceLabelId) {
|
||||
$coolifyName = data_get($labels, 'coolify.name');
|
||||
$serviceName = Str::of($coolifyName)->before('-');
|
||||
$serviceUuid = Str::of($coolifyName)->after('-');
|
||||
$service = $services->where('uuid', $serviceUuid)->first();
|
||||
$subType = data_get($labels, 'coolify.service.subType');
|
||||
$subId = data_get($labels, 'coolify.service.subId');
|
||||
$service = $services->where('id', $serviceLabelId)->first();
|
||||
if ($subType === 'application') {
|
||||
$service = $service->applications()->where('id', $subId)->first();
|
||||
} else {
|
||||
$service = $service->databases()->where('id', $subId)->first();
|
||||
}
|
||||
if ($service) {
|
||||
$foundService = $service->byName($serviceName);
|
||||
if ($foundService) {
|
||||
$foundServices[] = "$foundService->id-$serviceName";
|
||||
$statusFromDb = $foundService->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$foundService->update(['status' => $containerStatus]);
|
||||
}
|
||||
$foundServices[] = "$service->id-$service->name";
|
||||
$statusFromDb = $service->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$service->update(['status' => $containerStatus]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,8 @@ public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
||||
}
|
||||
public function workdir() {
|
||||
public function workdir()
|
||||
{
|
||||
return service_configuration_dir() . "/{$this->uuid}";
|
||||
}
|
||||
public function saveComposeConfigs()
|
||||
@ -97,6 +98,16 @@ public function saveComposeConfigs()
|
||||
}
|
||||
instant_remote_process($commands, $this->server);
|
||||
}
|
||||
private function generateFqdn($serviceVariables, $serviceName)
|
||||
{
|
||||
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";
|
||||
}
|
||||
}
|
||||
return $defaultUsableFqdn ?? null;
|
||||
}
|
||||
public function parse(bool $isNew = false): Collection
|
||||
{
|
||||
ray('parsing');
|
||||
@ -132,20 +143,27 @@ public function parse(bool $isNew = false): Collection
|
||||
data_set($service, 'is_database', true);
|
||||
}
|
||||
}
|
||||
if ($isNew) {
|
||||
if ($isDatabase) {
|
||||
$savedService = ServiceDatabase::where([
|
||||
'name' => $serviceName,
|
||||
'service_id' => $this->id
|
||||
])->first();
|
||||
} else {
|
||||
$savedService = ServiceApplication::where([
|
||||
'name' => $serviceName,
|
||||
'service_id' => $this->id
|
||||
])->first();
|
||||
}
|
||||
if ($isNew || is_null($savedService)) {
|
||||
if ($isDatabase) {
|
||||
$savedService = ServiceDatabase::create([
|
||||
'name' => $serviceName,
|
||||
'service_id' => $this->id
|
||||
]);
|
||||
} else {
|
||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.{$this->server->ip}.sslip.io";
|
||||
if (isDev()) {
|
||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.127.0.0.1.sslip.io";
|
||||
}
|
||||
$savedService = ServiceApplication::create([
|
||||
'name' => $serviceName,
|
||||
'fqdn' => $defaultUsableFqdn,
|
||||
'fqdn' => $this->generateFqdn($serviceVariables, $serviceName),
|
||||
'service_id' => $this->id
|
||||
]);
|
||||
}
|
||||
@ -157,14 +175,9 @@ public function parse(bool $isNew = false): Collection
|
||||
if (data_get($savedService, 'fqdn')) {
|
||||
$defaultUsableFqdn = data_get($savedService, 'fqdn', null);
|
||||
} else {
|
||||
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 = $this->generateFqdn($serviceVariables, $serviceName);
|
||||
}
|
||||
$savedService->fqdn = $defaultUsableFqdn ?? null;
|
||||
$savedService->fqdn = $defaultUsableFqdn;
|
||||
$savedService->save();
|
||||
}
|
||||
}
|
||||
@ -475,7 +488,7 @@ public function parse(bool $isNew = false): Collection
|
||||
// Add labels to the service
|
||||
$labels = collect(data_get($service, 'labels', []));
|
||||
$labels = collect([]);
|
||||
$labels = $labels->merge(defaultLabels($this->id, $container_name, type: 'service'));
|
||||
$labels = $labels->merge(defaultLabels($this->id, $container_name, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id));
|
||||
if (!$isDatabase) {
|
||||
if ($fqdns) {
|
||||
$labels = $labels->merge(fqdnLabelsForTraefik($fqdns, $container_name, true));
|
||||
|
@ -130,7 +130,7 @@ function get_port_from_dockerfile($dockerfile): int
|
||||
return 80;
|
||||
}
|
||||
|
||||
function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'application')
|
||||
function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null)
|
||||
{
|
||||
$labels = collect([]);
|
||||
$labels->push('coolify.managed=true');
|
||||
@ -141,6 +141,10 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
||||
if ($pull_request_id !== 0) {
|
||||
$labels->push('coolify.pullRequestId=' . $pull_request_id);
|
||||
}
|
||||
if ($type === 'service') {
|
||||
$labels->push('coolify.service.subId=' . $subId);
|
||||
$labels->push('coolify.service.subType=' . $subType);
|
||||
}
|
||||
return $labels;
|
||||
}
|
||||
function fqdnLabelsForTraefik(Collection $domains, $container_name, $is_force_https_enabled)
|
||||
|
@ -25,6 +25,9 @@ function serviceStatus(Service $service)
|
||||
$applications = $service->applications;
|
||||
$databases = $service->databases;
|
||||
foreach ($applications as $application) {
|
||||
if ($application->ignore_from_status) {
|
||||
continue;
|
||||
}
|
||||
if (Str::of($application->status)->startsWith('running')) {
|
||||
$foundRunning = true;
|
||||
} else {
|
||||
@ -32,6 +35,9 @@ function serviceStatus(Service $service)
|
||||
}
|
||||
}
|
||||
foreach ($databases as $database) {
|
||||
if ($database->ignore_from_status) {
|
||||
continue;
|
||||
}
|
||||
if (Str::of($database->status)->startsWith('running')) {
|
||||
$foundRunning = true;
|
||||
} else {
|
||||
|
@ -100,8 +100,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
|
||||
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
|
||||
}
|
||||
if (isset($livewire)) {
|
||||
$livewire->emit('error', $message);
|
||||
throw new RuntimeException($message);
|
||||
return $livewire->emit('error', $message);
|
||||
}
|
||||
|
||||
throw new RuntimeException($message);
|
||||
|
@ -14,6 +14,9 @@
|
||||
'expiration' => 10,
|
||||
],
|
||||
],
|
||||
'services' => [
|
||||
'offical' => 'https://cdn.coollabs.io/coolify/service-templates.json',
|
||||
],
|
||||
'limits' => [
|
||||
'trial_period'=> 7,
|
||||
'server' => [
|
||||
|
@ -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('service_applications', function (Blueprint $table) {
|
||||
$table->boolean('ignore_from_status')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('service_applications', function (Blueprint $table) {
|
||||
$table->dropColumn('ignore_from_status');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
<?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('service_databases', function (Blueprint $table) {
|
||||
$table->boolean('ignore_from_status')->default(false);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('service_databases', function (Blueprint $table) {
|
||||
$table->dropColumn('ignore_from_status');
|
||||
});
|
||||
}
|
||||
};
|
3
examples/service-templates.json
Normal file
3
examples/service-templates.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plausible-analytics": "dmVyc2lvbjogIjMuMyIKc2VydmljZXM6CiAgcGxhdXNpYmxlOgogICAgaW1hZ2U6IHBsYXVzaWJsZS9hbmFseXRpY3M6djIuMAogICAgY29tbWFuZDogc2ggLWMgInNsZWVwIDEwICYmIC9lbnRyeXBvaW50LnNoIGRiIGNyZWF0ZWRiICYmIC9lbnRyeXBvaW50LnNoIGRiIG1pZ3JhdGUgJiYgL2VudHJ5cG9pbnQuc2ggcnVuIgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vcG9zdGdyZXM6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNAcGxhdXNpYmxlX2RiL3BsYXVzaWJsZQogICAgICAtIEJBU0VfVVJMPSRTRVJWSUNFX0ZRRE5fUExBVVNJQkxFCiAgICAgIC0gU0VDUkVUX0tFWV9CQVNFPSRTRVJWSUNFX0JBU0U2NF82NF9QTEFVU0lCTEUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcGxhdXNpYmxlX2RiCiAgICAgIC0gcGxhdXNpYmxlX2V2ZW50c19kYgogICAgICAtIG1haWwKCiAgbWFpbDoKICAgIGltYWdlOiBieXRlbWFyay9zbXRwCgogIHBsYXVzaWJsZV9kYjoKICAgIGltYWdlOiBwb3N0Z3JlczoxNC1hbHBpbmUKICAgIHZvbHVtZXM6CiAgICAgIC0gZGItZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0RCPXBsYXVzaWJsZQogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCgogIHBsYXVzaWJsZV9ldmVudHNfZGI6CiAgICBpbWFnZTogY2xpY2tob3VzZS9jbGlja2hvdXNlLXNlcnZlcjoyMy4zLjcuNS1hbHBpbmUKICAgIHZvbHVtZXM6CiAgICAgIC0gdHlwZTogdm9sdW1lCiAgICAgICAgc291cmNlOiBldmVudC1kYXRhCiAgICAgICAgdGFyZ2V0OiAvdmFyL2xpYi9jbGlja2hvdXNlCiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9jbGlja2hvdXNlL2NsaWNraG91c2UtY29uZmlnLnhtbAogICAgICAgIHRhcmdldDogL2V0Yy9jbGlja2hvdXNlLXNlcnZlci9jb25maWcuZC9sb2dnaW5nLnhtbAogICAgICAgIHJlYWRfb25seTogdHJ1ZQogICAgICAgIGNvbnRlbnQ6ID4tCiAgICAgICAgICA8Y2xpY2tob3VzZT48cHJvZmlsZXM+PGRlZmF1bHQ+PGxvZ19xdWVyaWVzPjA8L2xvZ19xdWVyaWVzPjxsb2dfcXVlcnlfdGhyZWFkcz4wPC9sb2dfcXVlcnlfdGhyZWFkcz48L2RlZmF1bHQ+PC9wcm9maWxlcz48L2NsaWNraG91c2U+CiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9jbGlja2hvdXNlL2NsaWNraG91c2UtdXNlci1jb25maWcueG1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2NsaWNraG91c2Utc2VydmVyL3VzZXJzLmQvbG9nZ2luZy54bWwKICAgICAgICByZWFkX29ubHk6IHRydWUKICAgICAgICBjb250ZW50OiA+LQogICAgICAgICAgPGNsaWNraG91c2U+PGxvZ2dlcj48bGV2ZWw+d2FybmluZzwvbGV2ZWw+PGNvbnNvbGU+dHJ1ZTwvY29uc29sZT48L2xvZ2dlcj48cXVlcnlfdGhyZWFkX2xvZwogICAgICAgICAgcmVtb3ZlPSJyZW1vdmUiLz48cXVlcnlfbG9nIHJlbW92ZT0icmVtb3ZlIi8+PHRleHRfbG9nCiAgICAgICAgICByZW1vdmU9InJlbW92ZSIvPjx0cmFjZV9sb2cgcmVtb3ZlPSJyZW1vdmUiLz48bWV0cmljX2xvZwogICAgICAgICAgcmVtb3ZlPSJyZW1vdmUiLz48YXN5bmNocm9ub3VzX21ldHJpY19sb2cKICAgICAgICAgIHJlbW92ZT0icmVtb3ZlIi8+PHNlc3Npb25fbG9nIHJlbW92ZT0icmVtb3ZlIi8+PHBhcnRfbG9nCiAgICAgICAgICByZW1vdmU9InJlbW92ZSIvPjwvY2xpY2tob3VzZT4KICAgIHVsaW1pdHM6CiAgICAgIG5vZmlsZToKICAgICAgICBzb2Z0OiAyNjIxNDQKICAgICAgICBoYXJkOiAyNjIxNDQK"
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div>
|
||||
<div class="flex items-center p-1 px-2 mt-1 overflow-hidden transition-all transform rounded cursor-pointer bg-coolgray-200"
|
||||
<div >
|
||||
<div class="flex items-center p-1 px-2 overflow-hidden transition-all transform rounded cursor-pointer bg-coolgray-200"
|
||||
@click="showCommandPalette = true">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 icon" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
|
@ -1,5 +1,6 @@
|
||||
@auth
|
||||
<nav class="fixed h-full overflow-hidden overflow-y-auto scrollbar">
|
||||
<nav class="fixed h-full overflow-hidden overflow-y-auto pt-14 scrollbar">
|
||||
<a href="/" class="fixed top-0 z-50 mx-3 mt-3 cursor-pointer bg-coolgray-100"><img class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
|
||||
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
|
||||
<li title="Dashboard">
|
||||
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
|
||||
|
@ -1,5 +1,6 @@
|
||||
@auth
|
||||
<nav class="fixed h-full overflow-hidden overflow-y-auto pt-14 scrollbar">
|
||||
<nav class="fixed h-full overflow-hidden overflow-y-auto pt-28 scrollbar">
|
||||
<a href="/" class="fixed top-0 z-50 mx-3 mt-3 cursor-pointer bg-coolgray-100"><img class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
|
||||
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
|
||||
<li title="Dashboard">
|
||||
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
|
||||
@ -114,12 +115,13 @@ class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox=
|
||||
</li>
|
||||
@endif
|
||||
@if (isSubscriptionActive() || isDev())
|
||||
<li title="Send feedback or get help" class="fixed top-0 right-0 p-2 px-4 pt-2 mt-auto">
|
||||
<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">
|
||||
<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"/>
|
||||
<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>
|
||||
@endif
|
||||
|
@ -2,7 +2,7 @@
|
||||
@section('body')
|
||||
@parent
|
||||
<x-navbar />
|
||||
<div class="fixed top-3 left-4 z-50" id="vue">
|
||||
<div class="fixed z-50 top-[4.5rem] left-4" id="vue">
|
||||
<magic-bar></magic-bar>
|
||||
</div>
|
||||
<main class="main max-w-screen-2xl">
|
||||
|
@ -2,7 +2,7 @@
|
||||
@section('body')
|
||||
@parent
|
||||
@if (isSubscriptionOnGracePeriod())
|
||||
<div class="fixed top-3 left-4 z-50" id="vue">
|
||||
<div class="fixed top-[4.5rem] left-4 z-50" id="vue">
|
||||
<magic-bar></magic-bar>
|
||||
</div>
|
||||
<x-navbar />
|
||||
|
@ -15,7 +15,7 @@
|
||||
- SERVICE_BASE64_64_*: Generated 'base64' string with length of '64' (example: SERVICE_BASE64_64_GHOST, to generate 32 bit: SERVICE_BASE64_32_GHOST)<br>
|
||||
- SERVICE_USER_*: Generated user (example: SERVICE_USER_MYSQL)<br>
|
||||
- SERVICE_PASSWORD_*: Generated password (example: SERVICE_PASSWORD_MYSQL)<br>"
|
||||
rows="20" id="dockercompose"
|
||||
rows="20" id="dockerComposeRaw"
|
||||
placeholder='services:
|
||||
ghost:
|
||||
documentation: https://ghost.org/docs/config
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div x-data x-init="$wire.load_servers">
|
||||
<div x-data x-init="$wire.loadThings">
|
||||
<h1>New Resource</h1>
|
||||
<div class="pb-4 ">Deploy resources, like Applications, Databases, Services...</div>
|
||||
<div class="flex flex-col gap-2 pt-10">
|
||||
@ -52,18 +52,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (isDev())
|
||||
<div class="box group" wire:click="setType('dockercompose')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="group-hover:text-white">
|
||||
Based on a Docker Compose
|
||||
</div>
|
||||
<div class="text-xs group-hover:text-white">
|
||||
You can deploy complex application easily with Docker Compose.
|
||||
</div>
|
||||
<div class="box group" wire:click="setType('docker-compose-empty')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="group-hover:text-white">
|
||||
Based on a Docker Compose
|
||||
</div>
|
||||
<div class="text-xs group-hover:text-white">
|
||||
You can deploy complex application easily with Docker Compose.
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="py-4">Databases</h2>
|
||||
<div class="grid justify-start grid-cols-1 gap-2 text-left xl:grid-cols-3">
|
||||
@ -88,9 +86,28 @@
|
||||
</div>
|
||||
</div> --}}
|
||||
</div>
|
||||
<h2 class="py-4">Services</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2 class="py-4">Services</h2>
|
||||
<x-forms.button wire:click='loadServices(true)'>Reload Services from Templates</x-forms.button>
|
||||
</div>
|
||||
<div class="grid justify-start grid-cols-1 gap-2 text-left xl:grid-cols-3">
|
||||
Ghost, Plausible, Wordpress, etc... Coming very very soon...
|
||||
@if ($loadingServices)
|
||||
<span class="loading loading-xs loading-spinner"></span>
|
||||
@else
|
||||
@foreach ($services as $serviceName => $service)
|
||||
<div class="box group" wire:click="setType('one-click-service-{{ $serviceName }}')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="group-hover:text-white">
|
||||
{{ Str::headline($serviceName) }}
|
||||
</div>
|
||||
<div class="text-xs group-hover:text-white">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@endif
|
||||
@if ($current_step === 'servers')
|
||||
|
@ -7,13 +7,22 @@
|
||||
<h2>{{ Str::headline($application->name) }}</h2>
|
||||
@endif
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
<x-forms.button isError wire:click='delete'>Delete</x-forms.button>
|
||||
<a target="_blank" href="{{ $application->documentation() }}">Documentation <x-external-link /></a>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Name" id="application.human_name" placeholder="Human readable name"></x-forms.input>
|
||||
<x-forms.input label="Description" id="application.description"></x-forms.input>
|
||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
||||
id="application.fqdn"></x-forms.input>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Name" id="application.human_name"
|
||||
placeholder="Human readable name"></x-forms.input>
|
||||
<x-forms.input label="Description" id="application.description"></x-forms.input>
|
||||
</div>
|
||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains" id="application.fqdn"></x-forms.input>
|
||||
</div>
|
||||
<h3 class="pt-2">Advanced</h3>
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox instantSave label="Ignore 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>
|
||||
</div>
|
||||
</form>
|
||||
@if ($fileStorages->count() > 0)
|
||||
|
@ -13,6 +13,12 @@
|
||||
<x-forms.input label="Name" id="database.human_name" placeholder="Name"></x-forms.input>
|
||||
<x-forms.input label="Description" id="database.description"></x-forms.input>
|
||||
</div>
|
||||
<h3 class="pt-2">Advanced</h3>
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox instantSave label="Ignore 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>
|
||||
</div>
|
||||
</form>
|
||||
@if ($database->fileStorages()->get()->count() > 0)
|
||||
<h3 class="py-4">Mounted Files (binds)</h3>
|
||||
|
@ -29,9 +29,17 @@
|
||||
</div>
|
||||
</form>
|
||||
<h2 class="pb-4"> Service Stack </h2>
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
@foreach ($service->applications as $application)
|
||||
<a class="flex flex-col justify-center box"
|
||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-3 ">
|
||||
@foreach ($applications as $application)
|
||||
<a @class([
|
||||
'border-l border-dashed border-red-500' => Str::of(
|
||||
$application->status)->contains(['exited']),
|
||||
'border-l border-dashed border-success' => Str::of(
|
||||
$application->status)->contains(['running']),
|
||||
'border-l border-dashed border-warning' => Str::of(
|
||||
$application->status)->contains(['restarting']),
|
||||
'flex flex-col justify-center box',
|
||||
])
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
||||
@if ($application->human_name)
|
||||
{{ Str::headline($application->human_name) }}
|
||||
@ -44,10 +52,19 @@
|
||||
@if ($application->fqdn)
|
||||
<span class="text-xs">{{ $application->fqdn }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $application->status }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
@foreach ($service->databases as $database)
|
||||
<a class="flex flex-col justify-center box"
|
||||
@foreach ($databases as $database)
|
||||
<a @class([
|
||||
'border-l border-dashed border-red-500' => Str::of(
|
||||
$application->status)->contains(['exited']),
|
||||
'border-l border-dashed border-success' => Str::of(
|
||||
$application->status)->contains(['running']),
|
||||
'border-l border-dashed border-warning' => Str::of(
|
||||
$application->status)->contains(['restarting']),
|
||||
'flex flex-col justify-center box',
|
||||
])
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
||||
@if ($database->human_name)
|
||||
{{ Str::headline($database->human_name) }}
|
||||
@ -57,6 +74,7 @@
|
||||
@if ($database->description)
|
||||
<span class="text-xs">{{ $database->description }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $application->status }}</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@ -90,7 +108,8 @@
|
||||
</x-forms.textarea>
|
||||
</div>
|
||||
<div x-cloak x-show="raw === false">
|
||||
<x-forms.textarea label="Actual Docker Compose file that will be deployed" readonly rows="20" id="service.docker_compose">
|
||||
<x-forms.textarea label="Actual Docker Compose file that will be deployed" readonly
|
||||
rows="20" id="service.docker_compose">
|
||||
</x-forms.textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<livewire:project.new.github-private-repository-deploy-key :type="$type" />
|
||||
@elseif ($type === 'dockerfile')
|
||||
<livewire:project.new.simple-dockerfile :type="$type" />
|
||||
@elseif ($type === 'dockercompose')
|
||||
@elseif ($type === 'docker-compose-empty')
|
||||
<livewire:project.new.docker-compose :type="$type" />
|
||||
@else
|
||||
<livewire:project.new.select />
|
||||
|
Loading…
Reference in New Issue
Block a user