update boarding flow

This commit is contained in:
Andras Bacsai 2023-08-23 10:14:39 +02:00
parent b39ca51d41
commit d62af76097
17 changed files with 214 additions and 106 deletions

View File

@ -20,3 +20,4 @@ yarn-error.log
/.npm
/.bash_history
/_data
.rnd

1
.gitignore vendored
View File

@ -29,3 +29,4 @@ _ide_helper.php
.gitignore
.phpstorm.meta.php
_ide_helper_models.php
.rnd

View File

@ -12,29 +12,38 @@ public function __invoke(Server $server, Team $team)
{
$dockerVersion = '23.0';
$config = base64_encode('{ "live-restore": true }');
$activity = remote_process([
"echo ####### Installing Prerequisites...",
"command -v jq >/dev/null || apt-get update",
"command -v jq >/dev/null || apt install -y jq",
"echo ####### Installing/updating Docker Engine...",
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
"echo ####### Restarting Docker Engine...",
"systemctl restart docker",
"echo ####### Creating default network...",
"docker network create --attachable coolify",
"echo ####### Done!"
], $server);
StandaloneDocker::create([
'name' => 'coolify',
'network' => 'coolify',
'server_id' => $server->id,
'team_id' => $team->id
]);
if (is_dev()) {
$activity = remote_process([
"echo ####### Installing Prerequisites...",
"echo ####### Installing/updating Docker Engine...",
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
"echo ####### Restarting Docker Engine...",
], $server);
} else {
$activity = remote_process([
"echo ####### Installing Prerequisites...",
"command -v jq >/dev/null || apt-get update",
"command -v jq >/dev/null || apt install -y jq",
"echo ####### Installing/updating Docker Engine...",
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
"echo ####### Restarting Docker Engine...",
"systemctl restart docker",
"echo ####### Creating default network...",
"docker network create --attachable coolify",
"echo ####### Done!"
], $server);
StandaloneDocker::create([
'name' => 'coolify',
'network' => 'coolify',
'server_id' => $server->id,
]);
}
return $activity;
}

View File

@ -2,6 +2,7 @@
namespace App\Http\Livewire;
use App\Actions\Server\InstallDocker;
use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\Server;
@ -9,9 +10,7 @@
class Boarding extends Component
{
public string $currentState = 'create-private-key';
// public ?string $serverType = null;
public string $currentState = 'welcome';
public ?string $privateKeyType = null;
public ?string $privateKey = null;
@ -26,6 +25,8 @@ class Boarding extends Component
public ?string $remoteServerUser = 'root';
public ?Server $createdServer = null;
public ?Project $createdProject = null;
public function mount()
{
$this->privateKeyName = generate_random_name();
@ -64,7 +65,11 @@ public function skipBoarding()
public function setServer(string $type)
{
if ($type === 'localhost') {
$this->currentState = 'create-project';
$this->createdServer = Server::find(0);
if (!$this->createdServer) {
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
}
$this->currentState = 'select-proxy';
} elseif ($type === 'remote') {
$this->currentState = 'private-key';
}
@ -126,22 +131,50 @@ public function saveServer()
$this->currentState = 'install-docker';
return;
}
ray($uptime, $dockerVersion);
} catch (\Exception $e) {
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
}
}
public function installDocker()
{
$activity = resolve(InstallDocker::class)($this->createdServer, currentTeam());
$this->emit('newMonitorActivity', $activity->id);
$this->currentState = 'select-proxy';
}
public function selectProxy(string|null $proxyType = null)
{
if (!$proxyType) {
return $this->currentState = 'create-project';
}
$this->createdServer->proxy->type = $proxyType;
$this->createdServer->proxy->status = 'exited';
$this->createdServer->save();
$this->currentState = 'create-project';
}
public function createNewProject()
{
$this->createdProject = Project::create([
'name' => generate_random_name(),
'team_id' => currentTeam()->id
]);
$this->currentState = 'create-resource';
}
public function showNewResource()
{
$this->skipBoarding();
return redirect()->route(
'project.resources.new',
[
'project_uuid' => $this->createdProject->uuid,
'environment_name' => 'production',
]
);
}
private function createNewPrivateKey()
{
$this->privateKeyName = generate_random_name();
$this->privateKeyDescription = 'Created by Coolify';
$this->privateKey = generateSSHKey();
}
public function createNewProject()
{
Project::create([
'name' => generate_random_name(),
'team_id' => currentTeam()->id
]);
['private' => $this->privateKey] = generateSSHKey();
}
}

View File

@ -60,20 +60,19 @@ public function submit()
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
if ($found) {
$this->createNetworkAndAttachToProxy();
$this->addError('network', 'Network already added to this server.');
$this->emit('error', 'Network already added to this server.');
return;
} else {
$docker = ModelsStandaloneDocker::create([
'name' => $this->name,
'network' => $this->network,
'server_id' => $this->server_id,
'team_id' => currentTeam()->id
]);
}
$this->createNetworkAndAttachToProxy();
return redirect()->route('destination.show', $docker->uuid);
} catch (\Exception $e) {
return general_error_handler(err: $e);
return general_error_handler(err: $e, that: $this);
}
}

View File

@ -3,6 +3,7 @@
namespace App\Http\Livewire\Project\New;
use App\Models\Server;
use Countable;
use Livewire\Component;
class Select extends Component
@ -11,7 +12,7 @@ class Select extends Component
public string $type;
public string $server_id;
public string $destination_uuid;
public $servers = [];
public Countable|array|Server $servers;
public $destinations = [];
public array $parameters;
@ -23,6 +24,13 @@ public function mount()
public function set_type(string $type)
{
$this->type = $type;
if (count($this->servers) === 1) {
$server = $this->servers->first();
$this->set_server($server);
if (count($server->destinations()) === 1) {
$this->set_destination($server->destinations()->first()->uuid);
}
}
$this->current_step = 'servers';
}

View File

@ -35,7 +35,7 @@ public function change_proxy()
$this->emit('proxyStatusUpdated');
}
public function select_proxy(string $proxy_type)
public function select_proxy(ProxyTypes $proxy_type)
{
$this->server->proxy->type = $proxy_type;
$this->server->proxy->status = 'exited';

View File

@ -10,21 +10,30 @@ class Server extends BaseModel
{
use SchemalessAttributesTrait;
protected static function booted()
{
static::created(function ($server) {
ServerSetting::create([
'server_id' => $server->id,
]);
StandaloneDocker::create([
'name' => 'coolify',
'network' => 'coolify',
'server_id' => $server->id,
]);
});
static::deleting(function ($server) {
$server->settings()->delete();
});
}
public $casts = [
'proxy' => SchemalessAttributes::class,
];
protected $schemalessAttributes = [
'proxy',
];
protected $fillable = [
'name',
'ip',
'user',
'port',
'team_id',
'private_key_id',
'proxy',
];
protected $guarded = [];
static public function isReachable()
{
@ -51,17 +60,7 @@ static public function destinationsByServer(string $server_id)
return $standaloneDocker->concat($swarmDocker);
}
protected static function booted()
{
static::created(function ($server) {
ServerSetting::create([
'server_id' => $server->id,
]);
});
static::deleting(function ($server) {
$server->settings()->delete();
});
}
public function settings()
{

View File

@ -53,7 +53,7 @@ .icon:hover {
@apply text-white;
}
.box {
@apply flex items-center justify-center p-2 transition-colors rounded cursor-pointer min-h-12 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline;
@apply flex items-center p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
}
.lds-heart {

View File

@ -53,12 +53,12 @@
<span v-if="search"><span class="capitalize ">{{
sequenceState.sequence[sequenceState.currentActionIndex] }}</span> name
will be:
<x-highlighted text="{{ search }}" />
<span class="inline-block text-warning">{{ search }}</span>
</span>
<span v-else><span class="capitalize ">{{
sequenceState.sequence[sequenceState.currentActionIndex] }}</span> name
will be:
<x-highlighted text="randomly generated (type to change)" />
<span class="inline-block text-warning">randomly generated (type to change)</span>
</span>
</span>
</li>

View File

@ -1,3 +1,13 @@
<x-layout-simple>
<livewire:boarding />
<x-modal modalId="installDocker">
<x-slot:modalBody>
<livewire:activity-monitor header="Installing Docker Logs" />
</x-slot:modalBody>
<x-slot:modalSubmit>
<x-forms.button onclick="installDocker.close()" type="submit">
Close
</x-forms.button>
</x-slot:modalSubmit>
</x-modal>
</x-layout-simple>

View File

@ -1,5 +1,5 @@
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
<div class="box-border col-span-2 min-w-[24rem]">
<div class="box-border col-span-2 min-w-[24rem] min-h-[21rem]">
<h1 class="text-5xl font-bold">{{$title}}</h1>
<div class="py-6 ">
@isset($question)
@ -9,7 +9,7 @@
@endisset
</div>
@if($actions)
<div class="flex flex-col gap-4 md:flex-row">
<div class="flex flex-col flex-wrap gap-4 md:flex-row">
{{$actions}}
</div>
@endif

View File

@ -1,3 +1,4 @@
@php use App\Enums\ProxyTypes; @endphp
<div class="min-h-screen hero">
<div class="hero-content">
<div>
@ -5,7 +6,7 @@
<h1 class="text-5xl font-bold">Welcome to Coolify</h1>
<p class="py-6 text-xl text-center">Let me help you to set the basics.</p>
<div class="flex justify-center ">
<div class="w-72 box" wire:click="$set('currentState', 'select-server')">Get Started
<div class="justify-center box" wire:click="$set('currentState', 'select-server')">Get Started
</div>
</div>
@endif
@ -16,9 +17,9 @@
or on a <x-highlighted text="Remote Server" />?
</x-slot:question>
<x-slot:actions>
<div class="md:w-72 box" wire:click="setServer('localhost')">Localhost
<div class="justify-center box" wire:click="setServer('localhost')">Localhost
</div>
<div class="md:w-72 box" wire:click="setServer('remote')">Remote Server
<div class="justify-center box" wire:click="setServer('remote')">Remote Server
</div>
</x-slot:actions>
<x-slot:explanation>
@ -33,21 +34,21 @@
</x-boarding-step>
@endif
@if ($currentState === 'private-key')
<x-boarding-step title="Private Key">
<x-boarding-step title="SSH Key">
<x-slot:question>
Do you have your own Private Key?
Do you have your own SSH Private Key?
</x-slot:question>
<x-slot:actions>
<div class="md:w-72 box" wire:click="setPrivateKey('own')">Yes
<div class="justify-center box" wire:click="setPrivateKey('own')">Yes
</div>
<div class="md:w-72 box" wire:click="setPrivateKey('create')">No (create one for me)
<div class="justify-center box" wire:click="setPrivateKey('create')">No (create one for me)
</div>
</x-slot:actions>
<x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your private key to the remote server's
<code>~/.ssh/authorized_keys</code> file.
<p>SSH Keys are used to connect to a remote server through a secure shell, called SSH.</p>
<p>You can use your own ssh private key, or you can let Coolify to create one for you.</p>
<p>In both ways, you need to add the public version of your ssh private key to the remote server's
<code class="text-warning">~/.ssh/authorized_keys</code> file.
</p>
</x-slot:explanation>
</x-boarding-step>
@ -109,25 +110,53 @@
</x-boarding-step>
@endif
@if ($currentState === 'install-docker')
<x-boarding-step title="Install Docker">
<x-slot:question>
Could not find Docker Engine on your server. Do you want me to install it for you?
</x-slot:question>
<x-slot:actions>
<div class="w-72 box" wire:click="installDocker">Let's do it!</div>
</x-slot:actions>
<x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able to run optimal.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
<x-boarding-step title="Install Docker">
<x-slot:question>
Could not find Docker Engine on your server. Do you want me to install it for you?
</x-slot:question>
<x-slot:actions>
<div class="justify-center box" wire:click="installDocker" onclick="installDocker.showModal()">Let's do
it!</div>
</x-slot:actions>
<x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able
to run optimal.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
@if ($currentState === 'select-proxy')
<x-boarding-step title="Select a Proxy">
<x-slot:question>
If you would like to attach any kind of domain to your resources, you need a proxy.
</x-slot:question>
<x-slot:actions>
<x-forms.button wire:click="selectProxy" class="w-64 box">
Decide later
</x-forms.button>
<x-forms.button class="w-32 box" wire:click="selectProxy('{{ ProxyTypes::TRAEFIK_V2 }}')">
Traefik
v2
</x-forms.button>
<x-forms.button disabled class="w-32 box">
Nginx
</x-forms.button>
<x-forms.button disabled class="w-32 box">
Caddy
</x-forms.button>
</x-slot:actions>
<x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able
to run optimal.</p>
</x-slot:explanation>
</x-boarding-step>
@endif
@if ($currentState === 'create-project')
<x-boarding-step title="Project">
<x-slot:question>
I will create an initial project for you. You can change all the details later on.
</x-slot:question>
<x-slot:actions>
<div class="w-72 box" wire:click="createNewProject">Let's do it!</div>
<div class="justify-center box" wire:click="createNewProject">Let's do it!</div>
</x-slot:actions>
<x-slot:explanation>
<p>Projects are bound together several resources into one virtual group. There are no
@ -137,6 +166,20 @@
</x-slot:explanation>
</x-boarding-step>
@endif
@if ($currentState === 'create-resource')
<x-boarding-step title="Resources">
<x-slot:question>
I will redirect you to the new resource page, where you can create your first resource.
</x-slot:question>
<x-slot:actions>
<div class="justify-center box" wire:click="showNewResource">Let's do
it!</div>
</x-slot:actions>
<x-slot:explanation>
<p>A resource could be an application, a database or a service (like WordPress).</p>
</x-slot:explanation>
</x-boarding-step>
@endif
<div class="flex justify-center gap-2 pt-4">
<a wire:click='skipBoarding'>Skip boarding process</a>
<a wire:click='restartBoarding'>Restart boarding process</a>

View File

@ -18,18 +18,23 @@
<div class="">N/A</div>
@endforelse
</div>
<div class="grid gap-2 pt-2">
<div class="pt-2">
@if (count($networks) > 0)
<h4>Found Destinations</h4>
<h3 class="pb-4">Found Destinations</h3>
@endif
<div class="flex flex-wrap gap-2 ">
@foreach ($networks as $network)
<div >
<a
href="{{ route('destination.new', ['server_id' => $server->id, 'network_name' => data_get($network, 'Name')]) }}">
<x-forms.button>+<x-highlighted text="{{ data_get($network, 'Name') }}" />
<x-forms.button >+<x-highlighted text="{{ data_get($network, 'Name') }}" />
</x-forms.button>
</a>
</div>
@endforeach
</div>
</div>
@else
<div>Server is not validated. Validate first.</div>
@endif

View File

@ -3,9 +3,6 @@
<div class="flex items-end gap-2">
<h1>Project: {{ data_get($project, 'name') }}</h1>
<x-forms.button type="submit">Save</x-forms.button>
@if ($project->applications->count() === 0)
<livewire:project.delete-project :project_id="$project->id" />
@endif
</div>
<div class="pb-10">Edit project details here.</div>
<div class="flex gap-2">

View File

@ -10,7 +10,7 @@
</ul>
<h2>Applications</h2>
<div class="grid justify-start grid-cols-1 gap-2 text-left xl:grid-cols-3">
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 min-w-[24rem]"
<div class="box group"
wire:click="set_type('public')">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
@ -21,7 +21,7 @@
</div>
</div>
</div>
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 min-w-[24rem]"
<div class="box group"
wire:click="set_type('private-gh-app')">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
@ -32,7 +32,7 @@
</div>
</div>
</div>
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 min-w-[24rem]"
<div class="box group"
wire:click="set_type('private-deploy-key')">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
@ -45,7 +45,7 @@
</div>
</div>
<div class="grid justify-start grid-cols-1 gap-2 text-left xl:grid-cols-3">
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 min-w-[24rem]"
<div class="box group"
wire:click="set_type('dockerfile')">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
@ -59,7 +59,7 @@
</div>
<h2 class="py-4">Databases</h2>
<div class="flex flex-col justify-start gap-2 text-left xl:flex-row">
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200"
<div class="box group"
wire:click="set_type('postgresql')">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
@ -79,9 +79,9 @@
<li class="step step-secondary">Select a Server</li>
<li class="step">Select a Destination</li>
</ul>
<div class="flex flex-col justify-center gap-2 text-left xl:flex-row">
<div class="grid grid-cols-3 gap-2 text-left ">
@forelse($servers as $server)
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200"
<div class="box group"
wire:click="set_server({{ $server }})">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
@ -108,9 +108,9 @@
<li class="step step-secondary">Select a Server</li>
<li class="step step-secondary">Select a Destination</li>
</ul>
<div class="flex flex-col justify-center gap-2 text-left xl:flex-row">
<div class="grid grid-cols-3 gap-2 text-left ">
@foreach ($destinations as $destination)
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200"
<div class="box group"
wire:click="set_destination('{{ $destination->uuid }}')">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">

View File

@ -3,8 +3,11 @@
<h1>Environments</h1>
<x-forms.button class="btn" onclick="newEnvironment.showModal()">+ Add</x-forms.button>
<livewire:project.add-environment :project="$project" />
@if ($project->applications->count() === 0)
<livewire:project.delete-project :project_id="$project->id" />
@endif
</div>
<div class="subtitle text-xs truncate lg:text-sm">{{ $project->name }}</div>
<div class="text-xs truncate subtitle lg:text-sm">{{ $project->name }}</div>
<div class="grid gap-2 lg:grid-cols-2">
@forelse ($project->environments as $environment)
<a class="box" href="{{ route('project.resources', [$project->uuid, $environment->name]) }}">