mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-31 05:09:24 +00:00
Compare commits
92 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b9d4f9941 | ||
|
|
f49b87870c | ||
|
|
ed49d4e3a0 | ||
|
|
7811c75139 | ||
|
|
068a1b4bc4 | ||
|
|
c618d912db | ||
|
|
3d43f2127a | ||
|
|
79fde593a9 | ||
|
|
64ce41df0f | ||
|
|
23e205b6cd | ||
|
|
4161ea7eb6 | ||
|
|
77037f8933 | ||
|
|
bdcc0c8de5 | ||
|
|
ac133875fa | ||
|
|
f4819a849b | ||
|
|
2cd1785a92 | ||
|
|
797352a5db | ||
|
|
03a3405524 | ||
|
|
925326b885 | ||
|
|
ffdf51a490 | ||
|
|
272d5547c8 | ||
|
|
fbaec769f0 | ||
|
|
d93640a8d9 | ||
|
|
3749970831 | ||
|
|
7d45bb1335 | ||
|
|
da04e0c027 | ||
|
|
c637d2d90d | ||
|
|
1bb8860f37 | ||
|
|
38a22dcf4d | ||
|
|
91e1eb7664 | ||
|
|
c7946e7551 | ||
|
|
958645e37f | ||
|
|
acdfa89ec1 | ||
|
|
62ec85ee2e | ||
|
|
8f24a66456 | ||
|
|
0dd0888775 | ||
|
|
5040ddea28 | ||
|
|
1a04a57c01 | ||
|
|
9b6c162224 | ||
|
|
e22c5d22f5 | ||
|
|
84e5b39830 | ||
|
|
cdb6964b0b | ||
|
|
df4ecd47a7 | ||
|
|
4a84c7238a | ||
|
|
2b3057e1b4 | ||
|
|
ae65172946 | ||
|
|
3a2d17bc05 | ||
|
|
4c1067cf36 | ||
|
|
b046a3e9f7 | ||
|
|
199881c596 | ||
|
|
99c8607ff4 | ||
|
|
e61fcc77f9 | ||
|
|
20dca179fb | ||
|
|
0f542c65ae | ||
|
|
d609fcaee1 | ||
|
|
fe8a7fc54f | ||
|
|
398f122593 | ||
|
|
f0abdcc2da | ||
|
|
c9a278b750 | ||
|
|
6990c593a4 | ||
|
|
8f54b51ecd | ||
|
|
3eb628b773 | ||
|
|
fabb97330a | ||
|
|
03c9793d11 | ||
|
|
a4320b7cee | ||
|
|
cbf9bc99ea | ||
|
|
0fbc382467 | ||
|
|
0f8ccac775 | ||
|
|
ee20c3339e | ||
|
|
0b11093d18 | ||
|
|
de8118b59d | ||
|
|
58522b59b7 | ||
|
|
356394c03d | ||
|
|
3e4db2f5b2 | ||
|
|
872981b8b4 | ||
|
|
51c468ae0b | ||
|
|
80a797aec8 | ||
|
|
cfdab13d77 | ||
|
|
4fc8988ff4 | ||
|
|
ab5619292e | ||
|
|
b02e5d3f27 | ||
|
|
5f0c9c3a31 | ||
|
|
ea6ec07a45 | ||
|
|
36be325d0d | ||
|
|
6138ddcac6 | ||
|
|
0509da6730 | ||
|
|
5b877b84c2 | ||
|
|
0c35726a8d | ||
|
|
e74899611b | ||
|
|
e9149e534d | ||
|
|
7cded7a36d | ||
|
|
ba74d55b4c |
2
.github/workflows/coolify-helper-next.yml
vendored
2
.github/workflows/coolify-helper-next.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ "next" ]
|
branches: [ "next" ]
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper-next.yml
|
||||||
- docker/coolify-helper/Dockerfile
|
- docker/coolify-helper/Dockerfile
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|||||||
2
.github/workflows/coolify-helper.yml
vendored
2
.github/workflows/coolify-helper.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Create & publish manifest
|
- name: Create & publish manifest
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class StartProxy
|
|||||||
if (!$configuration) {
|
if (!$configuration) {
|
||||||
throw new \Exception("Configuration is not synced");
|
throw new \Exception("Configuration is not synced");
|
||||||
}
|
}
|
||||||
|
SaveConfiguration::run($server, $configuration);
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
@@ -50,12 +51,15 @@ class StartProxy
|
|||||||
"echo '####### Proxy installed successfully.'"
|
"echo '####### Proxy installed successfully.'"
|
||||||
]);
|
]);
|
||||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||||
if (!$async) {
|
if ($async) {
|
||||||
instant_remote_process($commands, $server);
|
|
||||||
return 'OK';
|
|
||||||
} else {
|
|
||||||
$activity = remote_process($commands, $server);
|
$activity = remote_process($commands, $server);
|
||||||
return $activity;
|
return $activity;
|
||||||
|
} else {
|
||||||
|
instant_remote_process($commands, $server);
|
||||||
|
$server->proxy->set('status', 'running');
|
||||||
|
$server->proxy->set('type', $proxyType);
|
||||||
|
$server->save();
|
||||||
|
return 'OK';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ class UpdateCoolify
|
|||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$localhost_name = 'localhost';
|
$this->server = Server::find(0)->first();
|
||||||
$this->server = Server::where('name', $localhost_name)->first();
|
|
||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,22 +10,16 @@ class StartService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
$workdir = service_configuration_dir() . "/{$service->uuid}";
|
$service->saveComposeConfigs();
|
||||||
|
$commands[] = "cd " . $service->workdir();
|
||||||
|
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||||
|
$commands[] = "echo '####### Creating Docker network.'";
|
||||||
|
$commands[] = "docker network create --attachable {$service->uuid} >/dev/null 2>/dev/null || true";
|
||||||
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
||||||
$commands[] = "echo '####### Pulling images.'";
|
$commands[] = "echo '####### Pulling images.'";
|
||||||
$commands[] = "mkdir -p $workdir";
|
$commands[] = "docker compose pull";
|
||||||
$commands[] = "cd $workdir";
|
|
||||||
|
|
||||||
$docker_compose_base64 = base64_encode($service->docker_compose);
|
|
||||||
$commands[] = "echo $docker_compose_base64 | base64 -d > docker-compose.yml";
|
|
||||||
$envs = $service->environment_variables()->get();
|
|
||||||
$commands[] = "rm -f .env || true";
|
|
||||||
foreach ($envs as $env) {
|
|
||||||
$commands[] = "echo '{$env->key}={$env->value}' >> .env";
|
|
||||||
}
|
|
||||||
$commands[] = "docker compose pull --quiet";
|
|
||||||
$commands[] = "echo '####### Starting containers.'";
|
$commands[] = "echo '####### Starting containers.'";
|
||||||
$commands[] = "docker compose up -d >/dev/null 2>&1";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
||||||
$activity = remote_process($commands, $service->server);
|
$activity = remote_process($commands, $service->server);
|
||||||
return $activity;
|
return $activity;
|
||||||
|
|||||||
@@ -21,5 +21,6 @@ class StopService
|
|||||||
$db->update(['status' => 'exited']);
|
$db->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy 2>/dev/null"], $service->server, false);
|
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy 2>/dev/null"], $service->server, false);
|
||||||
|
instant_remote_process(["docker network rm {$service->uuid} 2>/dev/null"], $service->server, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class Emails extends Command
|
|||||||
$type = select(
|
$type = select(
|
||||||
'Which Email should be sent?',
|
'Which Email should be sent?',
|
||||||
options: [
|
options: [
|
||||||
|
'updates' => 'Send Update Email to all users',
|
||||||
'emails-test' => 'Test',
|
'emails-test' => 'Test',
|
||||||
'application-deployment-success' => 'Application - Deployment Success',
|
'application-deployment-success' => 'Application - Deployment Success',
|
||||||
'application-deployment-failed' => 'Application - Deployment Failed',
|
'application-deployment-failed' => 'Application - Deployment Failed',
|
||||||
@@ -69,7 +70,7 @@ class Emails extends Command
|
|||||||
'realusers-server-lost-connection' => 'REAL - Server Lost Connection',
|
'realusers-server-lost-connection' => 'REAL - Server Lost Connection',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$emailsGathered = ['realusers-before-trial','realusers-server-lost-connection'];
|
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
||||||
if (!in_array($type, $emailsGathered)) {
|
if (!in_array($type, $emailsGathered)) {
|
||||||
$this->email = text('Email Address to send to');
|
$this->email = text('Email Address to send to');
|
||||||
}
|
}
|
||||||
@@ -78,6 +79,38 @@ class Emails extends Command
|
|||||||
$this->mail = new MailMessage();
|
$this->mail = new MailMessage();
|
||||||
$this->mail->subject("Test Email");
|
$this->mail->subject("Test Email");
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
|
case 'updates':
|
||||||
|
$teams = Team::all();
|
||||||
|
if (!$teams || $teams->isEmpty()) {
|
||||||
|
echo 'No teams found.' . PHP_EOL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$emails = [];
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
foreach ($team->members as $member) {
|
||||||
|
if ($member->email && $member->marketing_emails) {
|
||||||
|
$emails[] = $member->email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$emails = array_unique($emails);
|
||||||
|
$this->info("Sending to " . count($emails) . " emails.");
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->info($email);
|
||||||
|
}
|
||||||
|
$confirmed = confirm('Are you sure?');
|
||||||
|
if ($confirmed) {
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->subject('One-click Services, Docker Compose support');
|
||||||
|
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||||
|
'token' => encrypt($email),
|
||||||
|
]);
|
||||||
|
$this->mail->view('emails.updates',["unsubscribeUrl" => $unsubscribeUrl]);
|
||||||
|
$this->sendEmail($email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'emails-test':
|
case 'emails-test':
|
||||||
$this->mail = (new Test())->toMail();
|
$this->mail = (new Test())->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
@@ -141,20 +174,20 @@ class Emails extends Command
|
|||||||
$this->mail = (new BackupSuccess($backup, $db))->toMail();
|
$this->mail = (new BackupSuccess($backup, $db))->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
// case 'invitation-link':
|
// case 'invitation-link':
|
||||||
// $user = User::all()->first();
|
// $user = User::all()->first();
|
||||||
// $invitation = TeamInvitation::whereEmail($user->email)->first();
|
// $invitation = TeamInvitation::whereEmail($user->email)->first();
|
||||||
// if (!$invitation) {
|
// if (!$invitation) {
|
||||||
// $invitation = TeamInvitation::create([
|
// $invitation = TeamInvitation::create([
|
||||||
// 'uuid' => Str::uuid(),
|
// 'uuid' => Str::uuid(),
|
||||||
// 'email' => $user->email,
|
// 'email' => $user->email,
|
||||||
// 'team_id' => 1,
|
// 'team_id' => 1,
|
||||||
// 'link' => 'http://example.com',
|
// 'link' => 'http://example.com',
|
||||||
// ]);
|
// ]);
|
||||||
// }
|
// }
|
||||||
// $this->mail = (new InvitationLink($user))->toMail();
|
// $this->mail = (new InvitationLink($user))->toMail();
|
||||||
// $this->sendEmail();
|
// $this->sendEmail();
|
||||||
// break;
|
// break;
|
||||||
case 'waitlist-invitation-link':
|
case 'waitlist-invitation-link':
|
||||||
$this->mail = new MailMessage();
|
$this->mail = new MailMessage();
|
||||||
$this->mail->view('emails.waitlist-invitation', [
|
$this->mail->view('emails.waitlist-invitation', [
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use Illuminate\Http\Client\PendingRequest;
|
|||||||
use Illuminate\Http\Client\Pool;
|
use Illuminate\Http\Client\Pool;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
|
||||||
class SyncBunny extends Command
|
class SyncBunny extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +16,7 @@ class SyncBunny extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'sync:bunny';
|
protected $signature = 'sync:bunny {--only-template} {--only-version}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -28,6 +30,9 @@ class SyncBunny extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
$that = $this;
|
||||||
|
$only_template = $this->option('only-template');
|
||||||
|
$only_version = $this->option('only-version');
|
||||||
$bunny_cdn = "https://cdn.coollabs.io";
|
$bunny_cdn = "https://cdn.coollabs.io";
|
||||||
$bunny_cdn_path = "coolify";
|
$bunny_cdn_path = "coolify";
|
||||||
$bunny_cdn_storage_name = "coolcdn";
|
$bunny_cdn_storage_name = "coolcdn";
|
||||||
@@ -39,51 +44,71 @@ class SyncBunny extends Command
|
|||||||
$install_script = "install.sh";
|
$install_script = "install.sh";
|
||||||
$upgrade_script = "upgrade.sh";
|
$upgrade_script = "upgrade.sh";
|
||||||
$production_env = ".env.production";
|
$production_env = ".env.production";
|
||||||
|
$service_template = "service-templates.json";
|
||||||
|
|
||||||
$versions = "versions.json";
|
$versions = "versions.json";
|
||||||
|
|
||||||
PendingRequest::macro('storage', function ($file) {
|
PendingRequest::macro('storage', function ($fileName) use($that) {
|
||||||
$headers = [
|
$headers = [
|
||||||
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
'Content-Type' => 'application/octet-stream'
|
'Content-Type' => 'application/octet-stream'
|
||||||
];
|
];
|
||||||
$fileStream = fopen($file, "r");
|
$fileStream = fopen($fileName, "r");
|
||||||
$file = fread($fileStream, filesize($file));
|
$file = fread($fileStream, filesize($fileName));
|
||||||
|
$that->info('Uploading: ' . $fileName);
|
||||||
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
||||||
});
|
});
|
||||||
PendingRequest::macro('purge', function ($url) {
|
PendingRequest::macro('purge', function ($url) use ($that) {
|
||||||
$headers = [
|
$headers = [
|
||||||
'AccessKey' => env('BUNNY_API_KEY'),
|
'AccessKey' => env('BUNNY_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
];
|
];
|
||||||
ray('Purging: ' . $url);
|
$that->info('Purging: ' . $url);
|
||||||
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
||||||
"url" => $url,
|
"url" => $url,
|
||||||
"async" => false
|
"async" => false
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
$confirmed = confirm('Are you sure you want to sync?');
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($only_template) {
|
||||||
|
Http::pool(fn (Pool $pool) => [
|
||||||
|
$pool->storage(fileName: "$parent_dir/templates/$service_template")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$service_template"),
|
||||||
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
||||||
|
]);
|
||||||
|
$this->info('Service template uploaded & purged...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($only_version) {
|
||||||
|
Http::pool(fn (Pool $pool) => [
|
||||||
|
$pool->storage(fileName: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
||||||
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
|
]);
|
||||||
|
$this->info('versions.json uploaded & purged...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(file: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
$pool->storage(fileName: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->storage(file: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
$pool->storage(fileName: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
||||||
$pool->storage(file: "$parent_dir/$production_env")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$production_env"),
|
$pool->storage(fileName: "$parent_dir/$production_env")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$production_env"),
|
||||||
$pool->storage(file: "$parent_dir/scripts/$upgrade_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$upgrade_script"),
|
$pool->storage(fileName: "$parent_dir/scripts/$upgrade_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$upgrade_script"),
|
||||||
$pool->storage(file: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
|
$pool->storage(fileName: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
|
||||||
$pool->storage(file: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
|
||||||
]);
|
]);
|
||||||
ray("{$bunny_cdn}/{$bunny_cdn_path}");
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$production_env"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$production_env"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
|
||||||
]);
|
]);
|
||||||
echo "All files uploaded & purged...\n";
|
$this->info("All files uploaded & purged...");
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo $e->getMessage();
|
$this->error("Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,21 +27,28 @@ class Kernel extends ConsoleKernel
|
|||||||
// $this->instance_auto_update($schedule);
|
// $this->instance_auto_update($schedule);
|
||||||
// $this->check_scheduled_backups($schedule);
|
// $this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
|
$this->cleanup_servers($schedule);
|
||||||
} else {
|
} else {
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
// $schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
||||||
$this->instance_auto_update($schedule);
|
$this->instance_auto_update($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
|
$this->cleanup_servers($schedule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function cleanup_servers($schedule)
|
||||||
|
{
|
||||||
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||||
ray($servers);
|
|
||||||
|
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ class Handler extends ExceptionHandler
|
|||||||
}
|
}
|
||||||
app('sentry')->configureScope(
|
app('sentry')->configureScope(
|
||||||
function (Scope $scope) {
|
function (Scope $scope) {
|
||||||
|
$email = auth()?->user() ? auth()->user()->email : 'guest';
|
||||||
|
$instanceAdmin = User::find(0)->email ?? 'admin@localhost';
|
||||||
$scope->setUser(
|
$scope->setUser(
|
||||||
[
|
[
|
||||||
'email' => auth()->user()->email,
|
'email' => $email,
|
||||||
'instanceAdmin' => User::find(0)->email
|
'instanceAdmin' => $instanceAdmin
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class ProjectController extends Controller
|
||||||
{
|
{
|
||||||
@@ -41,9 +45,10 @@ class ProjectController extends Controller
|
|||||||
|
|
||||||
public function new()
|
public function new()
|
||||||
{
|
{
|
||||||
$type = request()->query('type');
|
$services = getServiceTemplates();
|
||||||
|
$type = Str::of(request()->query('type'));
|
||||||
$destination_uuid = request()->query('destination');
|
$destination_uuid = request()->query('destination');
|
||||||
$server = requesT()->query('server');
|
$server_id = request()->query('server_id');
|
||||||
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
@@ -61,8 +66,71 @@ class ProjectController extends Controller
|
|||||||
'database_uuid' => $standalone_postgresql->uuid,
|
'database_uuid' => $standalone_postgresql->uuid,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
||||||
|
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||||
|
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||||
|
ray($oneClickServiceName);
|
||||||
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
|
if ($oneClickDotEnvs) {
|
||||||
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||||
|
}
|
||||||
|
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->name = "$oneClickServiceName-" . $service->uuid;
|
||||||
|
$service->save();
|
||||||
|
if ($oneClickDotEnvs?->count() > 0) {
|
||||||
|
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||||
|
$key = Str::before($value, '=');
|
||||||
|
$value = Str::of(Str::after($value, '='));
|
||||||
|
$generatedValue = $value;
|
||||||
|
if ($value->contains('SERVICE_')) {
|
||||||
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
switch ($command->value()) {
|
||||||
|
case 'PASSWORD':
|
||||||
|
$generatedValue = Str::password(symbols: false);
|
||||||
|
break;
|
||||||
|
case 'PASSWORD_64':
|
||||||
|
$generatedValue = Str::password(length: 64, symbols: false);
|
||||||
|
break;
|
||||||
|
case 'BASE64_64':
|
||||||
|
$generatedValue = Str::random(64);
|
||||||
|
break;
|
||||||
|
case 'BASE64_128':
|
||||||
|
$generatedValue = Str::random(128);
|
||||||
|
break;
|
||||||
|
case 'BASE64':
|
||||||
|
$generatedValue = Str::random(32);
|
||||||
|
break;
|
||||||
|
case 'USER':
|
||||||
|
$generatedValue = Str::random(16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $generatedValue,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$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', [
|
return view('project.new', [
|
||||||
'type' => $type
|
'type' => $type->value()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
public function selectExistingPrivateKey()
|
public function selectExistingPrivateKey()
|
||||||
{
|
{
|
||||||
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
||||||
|
$this->privateKey = $this->createdPrivateKey->private_key;
|
||||||
$this->currentState = 'create-server';
|
$this->currentState = 'create-server';
|
||||||
}
|
}
|
||||||
public function createNewServer()
|
public function createNewServer()
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace App\Http\Livewire;
|
|||||||
|
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class Help extends Component
|
class Help extends Component
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ class Help extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->rateLimit(1, 60);
|
$this->rateLimit(3, 60);
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
||||||
$debug = "Route: {$this->path}";
|
$debug = "Route: {$this->path}";
|
||||||
@@ -42,7 +42,7 @@ class Help extends Component
|
|||||||
);
|
);
|
||||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||||
$this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
|
$this->emit('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ class General extends Component
|
|||||||
public string $git_branch;
|
public string $git_branch;
|
||||||
public string|null $git_commit_sha;
|
public string|null $git_commit_sha;
|
||||||
public string $build_pack;
|
public string $build_pack;
|
||||||
public string|null $wildcard_domain = null;
|
|
||||||
public string|null $server_wildcard_domain = null;
|
|
||||||
public string|null $global_wildcard_domain = null;
|
|
||||||
|
|
||||||
public bool $is_static;
|
public bool $is_static;
|
||||||
public bool $is_git_submodules_enabled;
|
public bool $is_git_submodules_enabled;
|
||||||
@@ -91,18 +88,20 @@ class General extends Component
|
|||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->checkWildCardDomain();
|
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkWildCardDomain()
|
public function getWildcardDomain() {
|
||||||
{
|
$server = data_get($this->application, 'destination.server');
|
||||||
$coolify_instance_settings = InstanceSettings::get();
|
if ($server) {
|
||||||
$this->server_wildcard_domain = data_get($this->application, 'destination.server.settings.wildcard_domain');
|
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||||
$this->global_wildcard_domain = data_get($coolify_instance_settings, 'wildcard_domain');
|
ray($fqdn);
|
||||||
$this->wildcard_domain = $this->server_wildcard_domain ?? $this->global_wildcard_domain ?? null;
|
$this->application->fqdn = $fqdn;
|
||||||
}
|
$this->application->save();
|
||||||
|
$this->emit('success', 'Application settings updated!');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->is_static = $this->application->settings->is_static;
|
$this->is_static = $this->application->settings->is_static;
|
||||||
@@ -112,31 +111,6 @@ class General extends Component
|
|||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
$this->checkWildCardDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateGlobalRandomDomain()
|
|
||||||
{
|
|
||||||
// Set wildcard domain based on Global wildcard domain
|
|
||||||
$url = Url::fromString($this->global_wildcard_domain);
|
|
||||||
$host = $url->getHost();
|
|
||||||
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
|
||||||
$scheme = $url->getScheme();
|
|
||||||
$this->application->fqdn = $scheme . '://' . $this->application->uuid . '.' . $host . $path;
|
|
||||||
$this->application->save();
|
|
||||||
$this->emit('success', 'Application settings updated!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateServerRandomDomain()
|
|
||||||
{
|
|
||||||
// Set wildcard domain based on Server wildcard domain
|
|
||||||
$url = Url::fromString($this->server_wildcard_domain);
|
|
||||||
$host = $url->getHost();
|
|
||||||
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
|
||||||
$scheme = $url->getScheme();
|
|
||||||
$this->application->fqdn = $scheme . '://' . $this->application->uuid . '.' . $host . $path;
|
|
||||||
$this->application->save();
|
|
||||||
$this->emit('success', 'Application settings updated!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class CreateScheduledBackup extends Component
|
|||||||
public $database;
|
public $database;
|
||||||
public $frequency;
|
public $frequency;
|
||||||
public bool $enabled = true;
|
public bool $enabled = true;
|
||||||
public bool $save_s3 = true;
|
public bool $save_s3 = false;
|
||||||
public $s3_storage_id;
|
public $s3_storage_id;
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
|
||||||
|
|||||||
@@ -2,73 +2,142 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\New;
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class DockerCompose extends Component
|
class DockerCompose extends Component
|
||||||
{
|
{
|
||||||
public string $dockercompose = '';
|
public string $dockerComposeRaw = '';
|
||||||
|
public string $envFile = '';
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->query = request()->query();
|
$this->query = request()->query();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->dockercompose = 'services:
|
$this->dockerComposeRaw = 'services:
|
||||||
ghost:
|
ghost:
|
||||||
documentation: https://ghost.org/docs/config
|
image: ghost:5
|
||||||
image: ghost:5
|
volumes:
|
||||||
volumes:
|
- ~/configs:/etc/configs/:ro
|
||||||
- ghost-content-data:/var/lib/ghost/content
|
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
|
||||||
environment:
|
- /var/lib/ghost/content:/tmp/ghost/content:rw
|
||||||
- url=$SERVICE_FQDN_GHOST
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
- database__client=mysql
|
- type: volume
|
||||||
- database__connection__host=mysql
|
source: mydata
|
||||||
- database__connection__user=$SERVICE_USER_MYSQL
|
target: /data
|
||||||
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
volume:
|
||||||
- database__connection__database=${MYSQL_DATABASE-ghost}
|
nocopy: true
|
||||||
depends_on:
|
- type: bind
|
||||||
- mysql
|
source: ./var/lib/ghost/data
|
||||||
mysql:
|
target: /data
|
||||||
documentation: https://hub.docker.com/_/mysql
|
- type: bind
|
||||||
image: mysql:8.0
|
source: /tmp
|
||||||
volumes:
|
target: /tmp
|
||||||
- ghost-mysql-data:/var/lib/mysql
|
labels:
|
||||||
environment:
|
- "test.label=true"
|
||||||
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
ports:
|
||||||
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
- "3000"
|
||||||
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
- "3000-3005"
|
||||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
|
- "8000:8000"
|
||||||
';
|
- "9090-9091:8080-8081"
|
||||||
|
- "49100:22"
|
||||||
|
- "127.0.0.1:8001:8001"
|
||||||
|
- "127.0.0.1:5000-5010:5000-5010"
|
||||||
|
- "127.0.0.1::5000"
|
||||||
|
- "6060:6060/udp"
|
||||||
|
- "12400-12500:1240"
|
||||||
|
- target: 80
|
||||||
|
published: 8080
|
||||||
|
protocol: tcp
|
||||||
|
mode: host
|
||||||
|
networks:
|
||||||
|
- some-network
|
||||||
|
- other-network
|
||||||
|
environment:
|
||||||
|
- database__client=${DATABASE_CLIENT:-mysql}
|
||||||
|
- database__connection__database=${MYSQL_DATABASE:-ghost}
|
||||||
|
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
|
||||||
|
- test=${TEST:?true}
|
||||||
|
- url=$SERVICE_FQDN_GHOST
|
||||||
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
volumes:
|
||||||
|
- ghost-mysql-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=$MYSQL_DATABASE
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||||
|
- SESSION_SECRET
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
environment:
|
||||||
|
RACK_ENV: development
|
||||||
|
A: $A
|
||||||
|
SHOW: ${SHOW}
|
||||||
|
SHOW1: ${SHOW2-show1}
|
||||||
|
SHOW2: ${SHOW3:-show2}
|
||||||
|
SHOW3: ${SHOW4?show3}
|
||||||
|
SHOW4: ${SHOW5:?show4}
|
||||||
|
SHOW5: ${SERVICE_USER_MINIO}
|
||||||
|
SHOW6: ${SERVICE_PASSWORD_MINIO}
|
||||||
|
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
|
||||||
|
SHOW8: ${SERVICE_BASE64_64_MINIO}
|
||||||
|
SHOW9: ${SERVICE_BASE64_128_MINIO}
|
||||||
|
SHOW10: ${SERVICE_BASE64_MINIO}
|
||||||
|
SHOW11:
|
||||||
|
';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate([
|
try {
|
||||||
'dockercompose' => 'required'
|
$this->validate([
|
||||||
]);
|
'dockerComposeRaw' => 'required'
|
||||||
$server_id = $this->query['server_id'];
|
]);
|
||||||
|
$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();
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->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 = Service::create([
|
$service->parse(isNew: true);
|
||||||
'name' => 'service' . Str::random(10),
|
|
||||||
'docker_compose_raw' => $this->dockercompose,
|
|
||||||
'environment_id' => $environment->id,
|
|
||||||
'server_id' => (int) $server_id,
|
|
||||||
]);
|
|
||||||
$service->name = "service-$service->uuid";
|
|
||||||
|
|
||||||
$service->parse(isNew: true);
|
return redirect()->route('project.service', [
|
||||||
|
'service_uuid' => $service->uuid,
|
||||||
return redirect()->route('project.service', [
|
'environment_name' => $environment->name,
|
||||||
'service_uuid' => $service->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
]);
|
||||||
'project_uuid' => $project->uuid,
|
} catch (\Throwable $e) {
|
||||||
]);
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use App\Traits\SaveFromRedirect;
|
|||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
class GithubPrivateRepository extends Component
|
class GithubPrivateRepository extends Component
|
||||||
{
|
{
|
||||||
@@ -95,6 +96,7 @@ class GithubPrivateRepository extends Component
|
|||||||
$this->loadBranchByPage();
|
$this->loadBranchByPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->selected_branch_name = data_get($this->branches,'0.name');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loadBranchByPage()
|
protected function loadBranchByPage()
|
||||||
@@ -144,10 +146,9 @@ class GithubPrivateRepository extends Component
|
|||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
$application->fqdn = "http://{$application->uuid}.{$destination->server->ip}.sslip.io";
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
if (isDev()) {
|
$application->fqdn = $fqdn;
|
||||||
$application->fqdn = "http://{$application->uuid}.127.0.0.1.sslip.io";
|
|
||||||
}
|
|
||||||
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
private GithubApp|GitlabApp|null $git_source = null;
|
private GithubApp|GitlabApp|null $git_source = null;
|
||||||
private string $git_host;
|
private string $git_host;
|
||||||
private string $git_repository;
|
private string $git_repository;
|
||||||
private string $git_branch;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -96,7 +95,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$application_init = [
|
$application_init = [
|
||||||
'name' => generate_random_name(),
|
'name' => generate_random_name(),
|
||||||
'git_repository' => $this->git_repository,
|
'git_repository' => $this->git_repository,
|
||||||
'git_branch' => $this->git_branch,
|
'git_branch' => $this->branch,
|
||||||
'git_full_url' => "git@$this->git_host:$this->git_repository.git",
|
'git_full_url' => "git@$this->git_host:$this->git_repository.git",
|
||||||
'build_pack' => 'nixpacks',
|
'build_pack' => 'nixpacks',
|
||||||
'ports_exposes' => $this->port,
|
'ports_exposes' => $this->port,
|
||||||
@@ -112,10 +111,8 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
$application->fqdn = "http://{$application->uuid}.{$destination->server->ip}.sslip.io";
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
if (isDev()) {
|
$application->fqdn = $fqdn;
|
||||||
$application->fqdn = "http://{$application->uuid}.127.0.0.1.sslip.io";
|
|
||||||
}
|
|
||||||
$application->name = generate_random_name($application->uuid);
|
$application->name = generate_random_name($application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
@@ -134,11 +131,6 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
||||||
$this->git_host = $this->repository_url_parsed->getHost();
|
$this->git_host = $this->repository_url_parsed->getHost();
|
||||||
$this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2);
|
$this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2);
|
||||||
if ($this->branch) {
|
|
||||||
$this->git_branch = $this->branch;
|
|
||||||
} else {
|
|
||||||
$this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->git_host == 'github.com') {
|
if ($this->git_host == 'github.com') {
|
||||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||||
|
|||||||
@@ -76,13 +76,14 @@ class PublicGitRepository extends Component
|
|||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
$this->selected_branch = $this->git_branch;
|
$this->selected_branch = $this->git_branch;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||||
}
|
try {
|
||||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
$this->git_branch = 'master';
|
||||||
try {
|
$this->get_branch();
|
||||||
$this->git_branch = 'master';
|
} catch (\Throwable $e) {
|
||||||
$this->get_branch();
|
return handleError($e, $this);
|
||||||
} catch (\Throwable $e) {
|
}
|
||||||
|
} else {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,10 +157,8 @@ class PublicGitRepository extends Component
|
|||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
$application->fqdn = "http://{$application->uuid}.{$destination->server->ip}.sslip.io";
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
if (isDev()) {
|
$application->fqdn = $fqdn;
|
||||||
$application->fqdn = "http://{$application->uuid}.127.0.0.1.sslip.io";
|
|
||||||
}
|
|
||||||
$application->name = generate_application_name($this->git_repository, $this->git_branch, $application->uuid);
|
$application->name = generate_application_name($this->git_repository, $this->git_branch, $application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
namespace App\Http\Livewire\Project\New;
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
|
||||||
use App\Models\SwarmDocker;
|
|
||||||
use Countable;
|
use Countable;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class Select extends Component
|
class Select extends Component
|
||||||
{
|
{
|
||||||
@@ -21,12 +21,16 @@ class Select extends Component
|
|||||||
public Collection|array $standaloneDockers = [];
|
public Collection|array $standaloneDockers = [];
|
||||||
public Collection|array $swarmDockers = [];
|
public Collection|array $swarmDockers = [];
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
public Collection|array $services = [];
|
||||||
|
public bool $loadingServices = true;
|
||||||
|
public bool $loading = false;
|
||||||
|
|
||||||
public ?string $existingPostgresqlUrl = null;
|
public ?string $existingPostgresqlUrl = null;
|
||||||
|
|
||||||
protected $queryString = [
|
protected $queryString = [
|
||||||
'server',
|
'server',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
@@ -44,9 +48,31 @@ class Select extends Component
|
|||||||
// return handleError($e, $this);
|
// return handleError($e, $this);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
public function loadThings()
|
||||||
|
{
|
||||||
|
$this->loadServices();
|
||||||
|
$this->loadServers();
|
||||||
|
}
|
||||||
|
public function loadServices(bool $forceReload = false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($forceReload) {
|
||||||
|
Cache::forget('services');
|
||||||
|
}
|
||||||
|
$this->services = getServiceTemplates();
|
||||||
|
$this->emit('success', 'Successfully loaded services.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->loadingServices = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function setType(string $type)
|
public function setType(string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
if ($this->loading) return;
|
||||||
|
$this->loading = true;
|
||||||
if ($type === "existing-postgresql") {
|
if ($type === "existing-postgresql") {
|
||||||
$this->current_step = $type;
|
$this->current_step = $type;
|
||||||
return;
|
return;
|
||||||
@@ -87,7 +113,7 @@ class Select extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load_servers()
|
public function loadServers()
|
||||||
{
|
{
|
||||||
$this->servers = Server::isUsable()->get();
|
$this->servers = Server::isUsable()->get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,10 +60,7 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$fqdn = "http://{$application->uuid}.{$destination->server->ip}.sslip.io";
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
if (isDev()) {
|
|
||||||
$fqdn = "http://{$application->uuid}.127.0.0.1.sslip.io";
|
|
||||||
}
|
|
||||||
$application->update([
|
$application->update([
|
||||||
'name' => 'dockerfile-' . $application->uuid,
|
'name' => 'dockerfile-' . $application->uuid,
|
||||||
'fqdn' => $fqdn
|
'fqdn' => $fqdn
|
||||||
|
|||||||
@@ -8,23 +8,47 @@ use Livewire\Component;
|
|||||||
class Application extends Component
|
class Application extends Component
|
||||||
{
|
{
|
||||||
public ServiceApplication $application;
|
public ServiceApplication $application;
|
||||||
|
public $parameters;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.human_name' => 'nullable',
|
'application.human_name' => 'nullable',
|
||||||
'application.description' => 'nullable',
|
'application.description' => 'nullable',
|
||||||
'application.fqdn' => 'nullable',
|
'application.fqdn' => 'nullable',
|
||||||
|
'application.image' => 'required',
|
||||||
|
'application.exclude_from_status' => 'required|boolean',
|
||||||
|
'application.required_fqdn' => 'required|boolean',
|
||||||
];
|
];
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
ray($this->application->fileStorages()->get());
|
|
||||||
return view('livewire.project.service.application');
|
return view('livewire.project.service.application');
|
||||||
}
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
|
updateCompose($this->application);
|
||||||
|
$this->emit('success', 'Application saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->emit('generateDockerCompose');
|
$this->emit('generateDockerCompose');
|
||||||
}
|
}
|
||||||
|
|||||||
19
app/Http/Livewire/Project/Service/ComposeModal.php
Normal file
19
app/Http/Livewire/Project/Service/ComposeModal.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ComposeModal extends Component
|
||||||
|
{
|
||||||
|
public string $raw;
|
||||||
|
public string $actual;
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.compose-modal');
|
||||||
|
}
|
||||||
|
public function submit() {
|
||||||
|
$this->emit('warning', "Saving new docker compose...");
|
||||||
|
$this->emit('saveCompose', $this->raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,26 +2,41 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
use App\Models\ServiceApplication;
|
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Database extends Component
|
class Database extends Component
|
||||||
{
|
{
|
||||||
public ServiceDatabase $database;
|
public ServiceDatabase $database;
|
||||||
|
public $fileStorages;
|
||||||
|
protected $listeners = ["refreshFileStorages"];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'database.human_name' => 'nullable',
|
'database.human_name' => 'nullable',
|
||||||
'database.description' => 'nullable',
|
'database.description' => 'nullable',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.exclude_from_status' => 'required|boolean',
|
||||||
];
|
];
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.database');
|
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()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
|
updateCompose($this->database);
|
||||||
|
$this->emit('success', 'Database saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -3,17 +3,56 @@
|
|||||||
namespace App\Http\Livewire\Project\Service;
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
use App\Models\LocalFileVolume;
|
use App\Models\LocalFileVolume;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class FileStorage extends Component
|
class FileStorage extends Component
|
||||||
{
|
{
|
||||||
public LocalFileVolume $fileStorage;
|
public LocalFileVolume $fileStorage;
|
||||||
|
public ServiceApplication|ServiceDatabase $service;
|
||||||
|
public string $fs_path;
|
||||||
|
public ?string $workdir = null;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
'fileStorage.is_directory' => 'required',
|
||||||
'fileStorage.fs_path' => 'required',
|
'fileStorage.fs_path' => 'required',
|
||||||
'fileStorage.mount_path' => 'required',
|
'fileStorage.mount_path' => 'required',
|
||||||
'fileStorage.content' => 'nullable',
|
'fileStorage.content' => 'nullable',
|
||||||
];
|
];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->service = $this->fileStorage->service;
|
||||||
|
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
||||||
|
$this->workdir = $this->service->service->workdir();
|
||||||
|
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
|
||||||
|
} else {
|
||||||
|
$this->workdir = null;
|
||||||
|
$this->fs_path = $this->fileStorage->fs_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$original = $this->fileStorage->getOriginal();
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->fileStorage->is_directory) {
|
||||||
|
$this->fileStorage->content = null;
|
||||||
|
}
|
||||||
|
$this->fileStorage->save();
|
||||||
|
$this->fileStorage->saveStorageOnServer($this->service);
|
||||||
|
$this->emit('success', 'File updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->fileStorage->setRawAttributes($original);
|
||||||
|
$this->fileStorage->save();
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.file-storage');
|
return view('livewire.project.service.file-storage');
|
||||||
|
|||||||
@@ -2,45 +2,71 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public Service $service;
|
public Service $service;
|
||||||
|
public $applications;
|
||||||
|
public $databases;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'service.docker_compose_raw' => 'required',
|
'service.docker_compose_raw' => 'required',
|
||||||
'service.docker_compose' => 'required',
|
'service.docker_compose' => 'required',
|
||||||
'service.name' => 'required',
|
'service.name' => 'required',
|
||||||
'service.description' => 'required',
|
'service.description' => 'nullable',
|
||||||
];
|
];
|
||||||
|
protected $listeners = ["saveCompose"];
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.index');
|
||||||
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->query = request()->query();
|
$this->query = request()->query();
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$this->refreshStack();
|
||||||
}
|
}
|
||||||
public function render()
|
public function saveCompose($raw)
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.index');
|
$this->service->docker_compose_raw = $raw;
|
||||||
|
$this->submit();
|
||||||
}
|
}
|
||||||
public function save() {
|
public function checkStatus()
|
||||||
$this->service->save();
|
{
|
||||||
$this->service->parse();
|
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||||
$this->service->refresh();
|
$this->refreshStack();
|
||||||
$this->emit('refreshEnvs');
|
|
||||||
}
|
}
|
||||||
public function submit() {
|
public function refreshStack()
|
||||||
|
{
|
||||||
|
$this->applications = $this->service->applications->sort();
|
||||||
|
$this->applications->each(function ($application) {
|
||||||
|
$application->refresh();
|
||||||
|
});
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
|
$this->databases->each(function ($database) {
|
||||||
|
$database->refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->service->save();
|
$this->service->save();
|
||||||
|
$this->service->parse();
|
||||||
|
$this->service->refresh();
|
||||||
|
$this->service->saveComposeConfigs();
|
||||||
|
$this->refreshStack();
|
||||||
|
$this->emit('refreshEnvs');
|
||||||
|
$this->emit('success', 'Service saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,5 +39,6 @@ class Navbar extends Component
|
|||||||
{
|
{
|
||||||
StopService::run($this->service);
|
StopService::run($this->service);
|
||||||
$this->service->refresh();
|
$this->service->refresh();
|
||||||
|
$this->emit('success', 'Service stopped successfully.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use Livewire\Component;
|
|||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public Service $service;
|
public Service $service;
|
||||||
public ServiceApplication $serviceApplication;
|
public ?ServiceApplication $serviceApplication = null;
|
||||||
public ServiceDatabase $serviceDatabase;
|
public ?ServiceDatabase $serviceDatabase = null;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
public Collection $services;
|
public Collection $services;
|
||||||
@@ -20,16 +20,26 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->services = collect([]);
|
try {
|
||||||
$this->parameters = get_route_parameters();
|
$this->services = collect([]);
|
||||||
$this->query = request()->query();
|
$this->parameters = get_route_parameters();
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
$this->query = request()->query();
|
||||||
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
if ($service) {
|
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
|
||||||
$this->serviceApplication = $service;
|
if ($service) {
|
||||||
} else {
|
$this->serviceApplication = $service;
|
||||||
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
$this->serviceApplication->getFilesFromServer();
|
||||||
|
} else {
|
||||||
|
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||||
|
$this->serviceDatabase->getFilesFromServer();
|
||||||
|
}
|
||||||
|
if (is_null($service)) {
|
||||||
|
throw new \Exception("Service not found.");
|
||||||
|
}
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public function generateDockerCompose()
|
public function generateDockerCompose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class Danger extends Component
|
|||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
|
// Should be queued
|
||||||
try {
|
try {
|
||||||
if ($this->resource->type() === 'service') {
|
if ($this->resource->type() === 'service') {
|
||||||
$server = $this->resource->server;
|
$server = $this->resource->server;
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ class Add extends Component
|
|||||||
public $parameters;
|
public $parameters;
|
||||||
public bool $is_preview = false;
|
public bool $is_preview = false;
|
||||||
public string $key;
|
public string $key;
|
||||||
public string $value;
|
public ?string $value = null;
|
||||||
public bool $is_build_time = false;
|
public bool $is_build_time = false;
|
||||||
|
|
||||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'key' => 'required|string',
|
'key' => 'required|string',
|
||||||
'value' => 'required|string',
|
'value' => 'nullable',
|
||||||
'is_build_time' => 'required|boolean',
|
'is_build_time' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -32,6 +32,7 @@ class Add extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
ray($this->key, $this->value, $this->is_build_time);
|
||||||
$this->emitUp('submit', [
|
$this->emitUp('submit', [
|
||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'value' => $this->value,
|
'value' => $this->value,
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ class All extends Component
|
|||||||
$this->resource->environment_variables_preview()->delete();
|
$this->resource->environment_variables_preview()->delete();
|
||||||
} else {
|
} else {
|
||||||
$variables = parseEnvFormatToArray($this->variables);
|
$variables = parseEnvFormatToArray($this->variables);
|
||||||
ray($variables);
|
|
||||||
$existingVariables = $this->resource->environment_variables();
|
$existingVariables = $this->resource->environment_variables();
|
||||||
$this->resource->environment_variables()->delete();
|
$this->resource->environment_variables()->delete();
|
||||||
}
|
}
|
||||||
@@ -110,11 +109,16 @@ class All extends Component
|
|||||||
$environment->is_build_time = $data['is_build_time'];
|
$environment->is_build_time = $data['is_build_time'];
|
||||||
$environment->is_preview = $data['is_preview'];
|
$environment->is_preview = $data['is_preview'];
|
||||||
|
|
||||||
if ($this->resource->type() === 'application') {
|
switch ($this->resource->type()) {
|
||||||
$environment->application_id = $this->resource->id;
|
case 'application':
|
||||||
}
|
$environment->application_id = $this->resource->id;
|
||||||
if ($this->resource->type() === 'standalone-postgresql') {
|
break;
|
||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
case 'standalone-postgresql':
|
||||||
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'service':
|
||||||
|
$environment->service_id = $this->resource->id;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$environment->save();
|
$environment->save();
|
||||||
$this->refreshEnvs();
|
$this->refreshEnvs();
|
||||||
|
|||||||
@@ -5,17 +5,19 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
|||||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public ModelsEnvironmentVariable $env;
|
public ModelsEnvironmentVariable $env;
|
||||||
public ?string $modalId = null;
|
public ?string $modalId = null;
|
||||||
|
public bool $isDisabled = false;
|
||||||
public string $type;
|
public string $type;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'env.key' => 'required|string',
|
'env.key' => 'required|string',
|
||||||
'env.value' => 'required|string',
|
'env.value' => 'nullable',
|
||||||
'env.is_build_time' => 'required|boolean',
|
'env.is_build_time' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -26,6 +28,10 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->isDisabled = false;
|
||||||
|
if (Str::of($this->env->key)->startsWith('SERVICE_FQDN') || Str::of($this->env->key)->startsWith('SERVICE_URL')) {
|
||||||
|
$this->isDisabled = true;
|
||||||
|
}
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class Show extends Component
|
|||||||
public LocalPersistentVolume $storage;
|
public LocalPersistentVolume $storage;
|
||||||
public bool $isReadOnly = false;
|
public bool $isReadOnly = false;
|
||||||
public ?string $modalId = null;
|
public ?string $modalId = null;
|
||||||
|
public ?string $realName = null;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'storage.name' => 'required|string',
|
'storage.name' => 'required|string',
|
||||||
@@ -25,6 +26,11 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
if ($this->storage->resource_type === 'App\Models\ServiceApplication' || $this->storage->resource_type === 'App\Models\ServiceDatabase') {
|
||||||
|
$this->realName = "{$this->storage->service->service->uuid}_{$this->storage->name}";
|
||||||
|
} else {
|
||||||
|
$this->realName = $this->storage->name;
|
||||||
|
}
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ class Proxy extends Component
|
|||||||
|
|
||||||
public function select_proxy($proxy_type)
|
public function select_proxy($proxy_type)
|
||||||
{
|
{
|
||||||
$this->server->proxy->type = $proxy_type;
|
$this->server->proxy->set('status', 'exited');
|
||||||
$this->server->proxy->status = 'exited';
|
$this->server->proxy->set('type', $proxy_type);
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->selectedProxy = $this->server->proxy->type;
|
$this->selectedProxy = $this->server->proxy->type;
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class Modal extends Component
|
|||||||
|
|
||||||
public function proxyStatusUpdated()
|
public function proxyStatusUpdated()
|
||||||
{
|
{
|
||||||
|
$this->server->proxy->set('status', 'running');
|
||||||
|
$this->server->save();
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,8 @@ class Status extends Component
|
|||||||
public function getProxyStatus()
|
public function getProxyStatus()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->server->isFunctional()) {
|
dispatch_sync(new ContainerStatusJob($this->server));
|
||||||
dispatch_sync(new ContainerStatusJob($this->server));
|
$this->emit('proxyStatusUpdated');
|
||||||
$this->emit('proxyStatusUpdated');
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ class ShowPrivateKey extends Component
|
|||||||
public function setPrivateKey($newPrivateKeyId)
|
public function setPrivateKey($newPrivateKeyId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
refresh_server_connection($this->server->privateKey);
|
|
||||||
$oldPrivateKeyId = $this->server->private_key_id;
|
$oldPrivateKeyId = $this->server->private_key_id;
|
||||||
|
refresh_server_connection($this->server->privateKey);
|
||||||
$this->server->update([
|
$this->server->update([
|
||||||
'private_key_id' => $newPrivateKeyId
|
'private_key_id' => $newPrivateKeyId
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -45,7 +45,12 @@ class Configuration extends Component
|
|||||||
$this->settings->do_not_track = $this->do_not_track;
|
$this->settings->do_not_track = $this->do_not_track;
|
||||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
||||||
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
||||||
$this->settings->next_channel = $this->next_channel;
|
if ($this->next_channel) {
|
||||||
|
$this->settings->next_channel = false;
|
||||||
|
$this->next_channel = false;
|
||||||
|
} else {
|
||||||
|
$this->settings->next_channel = $this->next_channel;
|
||||||
|
}
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->emit('success', 'Settings updated!');
|
$this->emit('success', 'Settings updated!');
|
||||||
}
|
}
|
||||||
@@ -68,7 +73,7 @@ class Configuration extends Component
|
|||||||
{
|
{
|
||||||
$file = "$this->dynamic_config_path/coolify.yaml";
|
$file = "$this->dynamic_config_path/coolify.yaml";
|
||||||
if (empty($this->settings->fqdn)) {
|
if (empty($this->settings->fqdn)) {
|
||||||
remote_process([
|
instant_remote_process([
|
||||||
"rm -f $file",
|
"rm -f $file",
|
||||||
], $this->server);
|
], $this->server);
|
||||||
} else {
|
} else {
|
||||||
@@ -124,7 +129,6 @@ class Configuration extends Component
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
||||||
dispatch(new ContainerStatusJob($this->server));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +141,7 @@ class Configuration extends Component
|
|||||||
$yaml;
|
$yaml;
|
||||||
|
|
||||||
$base64 = base64_encode($yaml);
|
$base64 = base64_encode($yaml);
|
||||||
remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $this->dynamic_config_path",
|
"mkdir -p $this->dynamic_config_path",
|
||||||
"echo '$base64' | base64 -d > $file",
|
"echo '$base64' | base64 -d > $file",
|
||||||
], $this->server);
|
], $this->server);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
||||||
if ($this->application->fqdn) {
|
if ($this->application->fqdn) {
|
||||||
$preview_fqdn = getOnlyFqdn(data_get($this->preview, 'fqdn'));
|
$preview_fqdn = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
|
||||||
$template = $this->application->preview_url_template;
|
$template = $this->application->preview_url_template;
|
||||||
$url = Url::fromString($this->application->fqdn);
|
$url = Url::fromString($this->application->fqdn);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
@@ -284,9 +284,20 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function rolling_update()
|
private function rolling_update()
|
||||||
{
|
{
|
||||||
$this->start_by_compose_file();
|
if (count($this->application->ports_mappings_array) > 0){
|
||||||
$this->health_check();
|
$this->execute_remote_command(
|
||||||
$this->stop_running_container();
|
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
||||||
|
);
|
||||||
|
$this->stop_running_container(force: true);
|
||||||
|
$this->start_by_compose_file();
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Rolling update started.'"],
|
||||||
|
);
|
||||||
|
$this->start_by_compose_file();
|
||||||
|
$this->health_check();
|
||||||
|
$this->stop_running_container();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function health_check()
|
private function health_check()
|
||||||
{
|
{
|
||||||
@@ -529,7 +540,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'environment' => $environment_variables,
|
'environment' => $environment_variables,
|
||||||
'labels' => generateLabelsApplication($this->application, $this->preview),
|
'labels' => generateLabelsApplication($this->application, $this->preview),
|
||||||
// 'expose' => $ports,
|
'expose' => $ports,
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->destination->network,
|
$this->destination->network,
|
||||||
],
|
],
|
||||||
@@ -704,10 +715,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stop_running_container()
|
private function stop_running_container(bool $force = false)
|
||||||
{
|
{
|
||||||
if ($this->currently_running_container_name) {
|
if ($this->currently_running_container_name) {
|
||||||
if ($this->newVersionIsHealthy) {
|
if ($this->newVersionIsHealthy || $force) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Removing old version of your application.'"],
|
["echo -n 'Removing old version of your application.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
||||||
@@ -724,7 +735,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
private function start_by_compose_file()
|
private function start_by_compose_file()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Rolling update started.'"],
|
["echo -n 'Starting application (could take a while).'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,12 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
|
$this->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [new WithoutOverlapping($this->server->uuid)];
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): string
|
public function uniqueId(): string
|
||||||
@@ -74,14 +75,15 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
$applications = $this->server->applications();
|
$applications = $this->server->applications();
|
||||||
$databases = $this->server->databases();
|
$databases = $this->server->databases();
|
||||||
$services = $this->server->services();
|
$services = $this->server->services()->get();
|
||||||
$previews = $this->server->previews();
|
$previews = $this->server->previews();
|
||||||
|
$this->server->proxyType();
|
||||||
/// Check if proxy is running
|
/// Check if proxy is running
|
||||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||||
return data_get($value, 'Name') === '/coolify-proxy';
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
})->first();
|
})->first();
|
||||||
if (!$foundProxyContainer) {
|
if (!$foundProxyContainer) {
|
||||||
|
ray('Proxy not found, starting it...');
|
||||||
if ($this->server->isProxyShouldRun()) {
|
if ($this->server->isProxyShouldRun()) {
|
||||||
StartProxy::run($this->server, false);
|
StartProxy::run($this->server, false);
|
||||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
@@ -99,7 +101,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$containerStatus = data_get($container, 'State.Status');
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
$containerHealth = data_get($container, 'State.Health.Status','unhealthy');
|
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||||
$containerStatus = "$containerStatus ($containerHealth)";
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
$labels = data_get($container, 'Config.Labels');
|
$labels = data_get($container, 'Config.Labels');
|
||||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
@@ -147,25 +149,29 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||||
if ($serviceLabelId) {
|
if ($serviceLabelId) {
|
||||||
$coolifyName = data_get($labels, 'coolify.name');
|
$subType = data_get($labels, 'coolify.service.subType');
|
||||||
$serviceName = Str::of($coolifyName)->before('-');
|
$subId = data_get($labels, 'coolify.service.subId');
|
||||||
$serviceUuid = Str::of($coolifyName)->after('-');
|
$service = $services->where('id', $serviceLabelId)->first();
|
||||||
$service = $services->where('uuid', $serviceUuid)->first();
|
if (!$service) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($subType === 'application') {
|
||||||
|
$service = $service->applications()->where('id', $subId)->first();
|
||||||
|
} else {
|
||||||
|
$service = $service->databases()->where('id', $subId)->first();
|
||||||
|
}
|
||||||
if ($service) {
|
if ($service) {
|
||||||
$foundService = $service->byName($serviceName);
|
$foundServices[] = "$service->id-$service->name";
|
||||||
if ($foundService) {
|
$statusFromDb = $service->status;
|
||||||
$foundServices[] = "$foundService->id-$serviceName";
|
if ($statusFromDb !== $containerStatus) {
|
||||||
$statusFromDb = $foundService->status;
|
// ray('Updating status: ' . $containerStatus);
|
||||||
if ($statusFromDb !== $containerStatus) {
|
$service->update(['status' => $containerStatus]);
|
||||||
// ray('Updating status: ' . $containerStatus);
|
|
||||||
$foundService->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$exitedServices = collect([]);
|
$exitedServices = collect([]);
|
||||||
foreach ($services->get() as $service) {
|
foreach ($services as $service) {
|
||||||
$apps = $service->applications()->get();
|
$apps = $service->applications()->get();
|
||||||
$dbs = $service->databases()->get();
|
$dbs = $service->databases()->get();
|
||||||
foreach ($apps as $app) {
|
foreach ($apps as $app) {
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (data_get($this->database, 'status') !== 'running') {
|
$status = Str::of(data_get($this->database, 'status'));
|
||||||
|
if (!$status->startsWith('running')) {
|
||||||
ray('database not running');
|
ray('database not running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,65 +16,66 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public $timeout = 500;
|
public $timeout = 1000;
|
||||||
public ?string $dockerRootFilesystem = null;
|
public ?string $dockerRootFilesystem = null;
|
||||||
public ?int $usageBefore = null;
|
public ?int $usageBefore = null;
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
(new WithoutOverlapping("dockerimagejobs"))->shared(),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
public function __construct()
|
|
||||||
|
public function uniqueId(): string
|
||||||
|
{
|
||||||
|
return $this->server->uuid;
|
||||||
|
}
|
||||||
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$queue = ApplicationDeploymentQueue::where('status', '==', 'in_progress')->get();
|
$queuedCount = 0;
|
||||||
if ($queue->count() > 0) {
|
$this->server->applications()->each(function ($application) use ($queuedCount) {
|
||||||
|
$count = data_get($application->deployments(), 'count', 0);
|
||||||
|
$queuedCount += $count;
|
||||||
|
});
|
||||||
|
if ($queuedCount > 0) {
|
||||||
ray('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping')->color('orange');
|
ray('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping')->color('orange');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// ray()->showQueries()->color('orange');
|
if (!$this->server->isFunctional()) {
|
||||||
$servers = Server::all();
|
return;
|
||||||
foreach ($servers as $server) {
|
}
|
||||||
if (
|
if (isDev()) {
|
||||||
!$server->isFunctional()
|
$this->dockerRootFilesystem = "/";
|
||||||
) {
|
} else {
|
||||||
continue;
|
$this->dockerRootFilesystem = instant_remote_process(
|
||||||
}
|
[
|
||||||
if (isDev()) {
|
"stat --printf=%m $(docker info --format '{{json .DockerRootDir}}'' |sed 's/\"//g')"
|
||||||
$this->dockerRootFilesystem = "/";
|
],
|
||||||
|
$this->server,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!$this->dockerRootFilesystem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->usageBefore = $this->getFilesystemUsage();
|
||||||
|
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
|
||||||
|
ray('Cleaning up ' . $this->server->name)->color('orange');
|
||||||
|
instant_remote_process(['docker image prune -af'], $this->server);
|
||||||
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $this->server);
|
||||||
|
instant_remote_process(['docker builder prune -af'], $this->server);
|
||||||
|
$usageAfter = $this->getFilesystemUsage();
|
||||||
|
if ($usageAfter < $this->usageBefore) {
|
||||||
|
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name)->color('orange');
|
||||||
|
send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
} else {
|
} else {
|
||||||
$this->dockerRootFilesystem = instant_remote_process(
|
ray('DockerCleanupJob failed to save disk space on ' . $this->server->name)->color('orange');
|
||||||
[
|
|
||||||
"stat --printf=%m $(docker info --format '{{json .DockerRootDir}}'' |sed 's/\"//g')"
|
|
||||||
],
|
|
||||||
$server,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!$this->dockerRootFilesystem) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->usageBefore = $this->getFilesystemUsage($server);
|
|
||||||
if ($this->usageBefore >= $server->settings->cleanup_after_percentage) {
|
|
||||||
ray('Cleaning up ' . $server->name)->color('orange');
|
|
||||||
instant_remote_process(['docker image prune -af'], $server);
|
|
||||||
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server);
|
|
||||||
instant_remote_process(['docker builder prune -af'], $server);
|
|
||||||
$usageAfter = $this->getFilesystemUsage($server);
|
|
||||||
if ($usageAfter < $this->usageBefore) {
|
|
||||||
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $server->name)->color('orange');
|
|
||||||
send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $server->name);
|
|
||||||
} else {
|
|
||||||
ray('DockerCleanupJob failed to save disk space on ' . $server->name)->color('orange');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ray('No need to clean up ' . $server->name)->color('orange');
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ray('No need to clean up ' . $this->server->name)->color('orange');
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
||||||
@@ -83,8 +84,8 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getFilesystemUsage(Server $server)
|
private function getFilesystemUsage()
|
||||||
{
|
{
|
||||||
return instant_remote_process(["df '{$this->dockerRootFilesystem}'| tail -1 | awk '{ print $5}' | sed 's/%//g'"], $server, false);
|
return instant_remote_process(["df '{$this->dockerRootFilesystem}'| tail -1 | awk '{ print $5}' | sed 's/%//g'"], $this->server, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,13 +212,9 @@ class Application extends BaseModel
|
|||||||
{
|
{
|
||||||
if (data_get($this, 'private_key_id')) {
|
if (data_get($this, 'private_key_id')) {
|
||||||
return 'deploy_key';
|
return 'deploy_key';
|
||||||
}
|
} else if (data_get($this, 'source')) {
|
||||||
if (data_get($this, 'source')) {
|
|
||||||
return 'source';
|
return 'source';
|
||||||
}
|
}
|
||||||
if (data_get($this, 'private_key_id')) {
|
|
||||||
return 'deploy_key';
|
|
||||||
}
|
|
||||||
throw new \Exception('No deployment type found');
|
throw new \Exception('No deployment type found');
|
||||||
}
|
}
|
||||||
public function could_set_build_commands(): bool
|
public function could_set_build_commands(): bool
|
||||||
@@ -230,7 +226,7 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
public function git_based(): bool
|
public function git_based(): bool
|
||||||
{
|
{
|
||||||
if ($this->dockerfile || $this->build_pack === 'dockerfile' || $this->dockercompose || $this->build_pack === 'dockercompose') {
|
if ($this->dockerfile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Environment extends Model
|
|||||||
|
|
||||||
public function can_delete_environment()
|
public function can_delete_environment()
|
||||||
{
|
{
|
||||||
return $this->applications()->count() == 0 && $this->postgresqls()->count() == 0;
|
return $this->applications()->count() == 0 && $this->postgresqls()->count() == 0 && $this->services()->count() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applications()
|
public function applications()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
|
|
||||||
class GithubApp extends BaseModel
|
class GithubApp extends BaseModel
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
protected $appends = ['type'];
|
protected $appends = ['type'];
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
@@ -17,6 +18,7 @@ class GithubApp extends BaseModel
|
|||||||
'webhook_secret',
|
'webhook_secret',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
static public function public()
|
static public function public()
|
||||||
{
|
{
|
||||||
return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get();
|
return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get();
|
||||||
@@ -34,6 +36,7 @@ class GithubApp extends BaseModel
|
|||||||
if ($applications_count > 0) {
|
if ($applications_count > 0) {
|
||||||
throw new \Exception('You cannot delete this GitHub App because it is in use by ' . $applications_count . ' application(s). Delete them first.');
|
throw new \Exception('You cannot delete this GitHub App because it is in use by ' . $applications_count . ' application(s). Delete them first.');
|
||||||
}
|
}
|
||||||
|
$github_app->privateKey()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class LocalFileVolume extends BaseModel
|
class LocalFileVolume extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -11,6 +12,42 @@ class LocalFileVolume extends BaseModel
|
|||||||
|
|
||||||
public function service()
|
public function service()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo('resource');
|
||||||
|
}
|
||||||
|
public function saveStorageOnServer(ServiceApplication|ServiceDatabase $service)
|
||||||
|
{
|
||||||
|
$workdir = $service->service->workdir();
|
||||||
|
$server = $service->service->server;
|
||||||
|
$commands = collect([
|
||||||
|
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||||
|
"cd $workdir"
|
||||||
|
]);
|
||||||
|
$fileVolume = $this;
|
||||||
|
$path = Str::of(data_get($fileVolume, 'fs_path'));
|
||||||
|
$content = data_get($fileVolume, 'content');
|
||||||
|
if ($path->startsWith('.')) {
|
||||||
|
$path = $path->after('.');
|
||||||
|
$path = $workdir . $path;
|
||||||
|
}
|
||||||
|
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
|
||||||
|
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
|
||||||
|
ray($isFile);
|
||||||
|
if ($isFile == 'OK' && $fileVolume->is_directory) {
|
||||||
|
throw new \Exception("File $path is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.");
|
||||||
|
} else if ($isDir == 'OK' && !$fileVolume->is_directory) {
|
||||||
|
throw new \Exception("File $path is a directory on the server, but you are trying to mark it as a file. Please delete the directory on the server or mark it as directory.");
|
||||||
|
}
|
||||||
|
if (($isFile == 'NOK' && !$fileVolume->is_directory) || $isFile == 'OK') {
|
||||||
|
$rootDir = Str::of($path)->dirname();
|
||||||
|
$commands->push("mkdir -p $rootDir > /dev/null 2>&1 || true");
|
||||||
|
$commands->push("touch $path > /dev/null 2>&1 || true");
|
||||||
|
if ($content) {
|
||||||
|
$content = base64_encode($content);
|
||||||
|
$commands->push("echo '$content' | base64 -d > $path");
|
||||||
|
}
|
||||||
|
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||||
|
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||||
|
}
|
||||||
|
return instant_remote_process($commands, $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,16 +12,19 @@ class LocalPersistentVolume extends Model
|
|||||||
|
|
||||||
public function application()
|
public function application()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo('resource');
|
||||||
}
|
}
|
||||||
public function service()
|
public function service()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo('resource');
|
||||||
|
}
|
||||||
|
public function database()
|
||||||
|
{
|
||||||
|
return $this->morphTo('resource');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function standalone_postgresql()
|
public function standalone_postgresql()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo('resource');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function name(): Attribute
|
protected function name(): Attribute
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class Server extends BaseModel
|
|||||||
return $this->hasOne(ServerSetting::class);
|
return $this->hasOne(ServerSetting::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function proxyType() {
|
public function proxyType()
|
||||||
|
{
|
||||||
$type = $this->proxy->get('type');
|
$type = $this->proxy->get('type');
|
||||||
if (is_null($type)) {
|
if (is_null($type)) {
|
||||||
$this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
$this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||||
@@ -115,11 +116,13 @@ class Server extends BaseModel
|
|||||||
return $standaloneDocker->applications;
|
return $standaloneDocker->applications;
|
||||||
})->flatten();
|
})->flatten();
|
||||||
}
|
}
|
||||||
public function services() {
|
public function services()
|
||||||
|
{
|
||||||
return $this->hasMany(Service::class);
|
return $this->hasMany(Service::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function previews() {
|
public function previews()
|
||||||
|
{
|
||||||
return $this->destinations()->map(function ($standaloneDocker) {
|
return $this->destinations()->map(function ($standaloneDocker) {
|
||||||
return $standaloneDocker->applications->map(function ($application) {
|
return $standaloneDocker->applications->map(function ($application) {
|
||||||
return $application->previews;
|
return $application->previews;
|
||||||
@@ -161,6 +164,9 @@ class Server extends BaseModel
|
|||||||
public function isProxyShouldRun()
|
public function isProxyShouldRun()
|
||||||
{
|
{
|
||||||
$shouldRun = false;
|
$shouldRun = false;
|
||||||
|
if ($this->proxyType() === ProxyTypes::NONE->value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
foreach ($this->applications() as $application) {
|
foreach ($this->applications() as $application) {
|
||||||
if (data_get($application, 'fqdn')) {
|
if (data_get($application, 'fqdn')) {
|
||||||
$shouldRun = true;
|
$shouldRun = true;
|
||||||
@@ -175,7 +181,8 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
return $shouldRun;
|
return $shouldRun;
|
||||||
}
|
}
|
||||||
public function isFunctional() {
|
public function isFunctional()
|
||||||
|
{
|
||||||
return $this->settings->is_reachable && $this->settings->is_usable;
|
return $this->settings->is_reachable && $this->settings->is_usable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
@@ -41,6 +41,7 @@ class Service extends BaseModel
|
|||||||
instant_remote_process(["docker volume rm -f $storage->name"], $service->server, false);
|
instant_remote_process(["docker volume rm -f $storage->name"], $service->server, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
instant_remote_process(["docker network rm {$service->uuid}"], $service->server, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public function type()
|
public function type()
|
||||||
@@ -48,6 +49,12 @@ class Service extends BaseModel
|
|||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function documentation()
|
||||||
|
{
|
||||||
|
$services = Cache::get('services', []);
|
||||||
|
$service = data_get($services, Str::of($this->name)->beforeLast('-')->value, []);
|
||||||
|
return data_get($service, 'documentation', config('constants.docs.base_url'));
|
||||||
|
}
|
||||||
public function applications()
|
public function applications()
|
||||||
{
|
{
|
||||||
return $this->hasMany(ServiceApplication::class);
|
return $this->hasMany(ServiceApplication::class);
|
||||||
@@ -80,9 +87,31 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
||||||
}
|
}
|
||||||
|
public function workdir()
|
||||||
|
{
|
||||||
|
return service_configuration_dir() . "/{$this->uuid}";
|
||||||
|
}
|
||||||
|
public function saveComposeConfigs()
|
||||||
|
{
|
||||||
|
$workdir = $this->workdir();
|
||||||
|
$commands[] = "mkdir -p $workdir";
|
||||||
|
$commands[] = "cd $workdir";
|
||||||
|
|
||||||
|
$docker_compose_base64 = base64_encode($this->docker_compose);
|
||||||
|
$commands[] = "echo $docker_compose_base64 | base64 -d > docker-compose.yml";
|
||||||
|
$envs = $this->environment_variables()->get();
|
||||||
|
$commands[] = "rm -f .env || true";
|
||||||
|
foreach ($envs as $env) {
|
||||||
|
$commands[] = "echo '{$env->key}={$env->value}' >> .env";
|
||||||
|
}
|
||||||
|
if ($envs->count() === 0) {
|
||||||
|
$commands[] = "touch .env";
|
||||||
|
}
|
||||||
|
instant_remote_process($commands, $this->server);
|
||||||
|
}
|
||||||
|
|
||||||
public function parse(bool $isNew = false): Collection
|
public function parse(bool $isNew = false): Collection
|
||||||
{
|
{
|
||||||
ray('parsing');
|
|
||||||
// ray()->clearAll();
|
// ray()->clearAll();
|
||||||
if ($this->docker_compose_raw) {
|
if ($this->docker_compose_raw) {
|
||||||
try {
|
try {
|
||||||
@@ -91,73 +120,101 @@ class Service extends BaseModel
|
|||||||
throw new \Exception($e->getMessage());
|
throw new \Exception($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$composeVolumes = collect(data_get($yaml, 'volumes', []));
|
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
|
||||||
$composeNetworks = collect(data_get($yaml, 'networks', []));
|
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||||
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
||||||
$services = data_get($yaml, 'services');
|
$services = data_get($yaml, 'services');
|
||||||
$definedNetwork = $this->uuid;
|
$definedNetwork = $this->uuid;
|
||||||
|
|
||||||
$volumes = collect([]);
|
$generatedServiceFQDNS = collect([]);
|
||||||
$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 ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS) {
|
||||||
$container_name = "$serviceName-{$this->uuid}";
|
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||||
$isDatabase = false;
|
$servicePorts = collect(data_get($service, 'ports', []));
|
||||||
|
$serviceNetworks = collect(data_get($service, 'networks', []));
|
||||||
$serviceVariables = collect(data_get($service, 'environment', []));
|
$serviceVariables = collect(data_get($service, 'environment', []));
|
||||||
|
$serviceLabels = collect(data_get($service, 'labels', []));
|
||||||
|
|
||||||
|
$containerName = "$serviceName-{$this->uuid}";
|
||||||
|
|
||||||
// Decide if the service is a database
|
// Decide if the service is a database
|
||||||
$image = data_get($service, 'image');
|
$isDatabase = false;
|
||||||
if ($image) {
|
$image = data_get_str($service, 'image');
|
||||||
$imageName = Str::of($image)->before(':');
|
if ($image->contains(':')) {
|
||||||
if (collect(DATABASE_DOCKER_IMAGES)->contains($imageName)) {
|
$image = Str::of($image);
|
||||||
$isDatabase = true;
|
} else {
|
||||||
data_set($service, 'is_database', true);
|
$image = Str::of($image)->append(':latest');
|
||||||
|
}
|
||||||
|
$imageName = $image->before(':');
|
||||||
|
|
||||||
|
if (collect(DATABASE_DOCKER_IMAGES)->contains($imageName)) {
|
||||||
|
$isDatabase = true;
|
||||||
|
}
|
||||||
|
data_set($service, 'is_database', $isDatabase);
|
||||||
|
|
||||||
|
// Create new serviceApplication or serviceDatabase
|
||||||
|
if ($isDatabase) {
|
||||||
|
if ($isNew) {
|
||||||
|
$savedService = ServiceDatabase::create([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceDatabase::where([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'service_id' => $this->id
|
||||||
|
])->first();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($isNew) {
|
||||||
|
$savedService = ServiceApplication::create([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
|
'service_id' => $this->id
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$savedService = ServiceApplication::where([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'service_id' => $this->id
|
||||||
|
])->first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($isNew) {
|
if (is_null($savedService)) {
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$savedService = ServiceDatabase::create([
|
$savedService = ServiceDatabase::create([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
|
'image' => $image,
|
||||||
'service_id' => $this->id
|
'service_id' => $this->id
|
||||||
]);
|
]);
|
||||||
} else {
|
} 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([
|
$savedService = ServiceApplication::create([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
'fqdn' => $defaultUsableFqdn,
|
'image' => $image,
|
||||||
'service_id' => $this->id
|
'service_id' => $this->id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if ($isDatabase) {
|
|
||||||
$savedService = $this->databases()->whereName($serviceName)->first();
|
// Check if image changed
|
||||||
} else {
|
if ($savedService->image !== $image) {
|
||||||
$savedService = $this->applications()->whereName($serviceName)->first();
|
$savedService->image = $image;
|
||||||
if (data_get($savedService, 'fqdn')) {
|
$savedService->save();
|
||||||
$defaultUsableFqdn = data_get($savedService, 'fqdn', null);
|
}
|
||||||
} else {
|
|
||||||
if (Str::of($serviceVariables)->contains('SERVICE_FQDN') || Str::of($serviceVariables)->contains('SERVICE_URL')) {
|
// Collect/create/update networks
|
||||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.{$this->server->ip}.sslip.io";
|
if ($serviceNetworks->count() > 0) {
|
||||||
if (isDev()) {
|
foreach ($serviceNetworks as $networkName => $networkDetails) {
|
||||||
$defaultUsableFqdn = "http://$serviceName-{$this->uuid}.127.0.0.1.sslip.io";
|
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
|
||||||
}
|
return $value == $networkName || $key == $networkName;
|
||||||
}
|
});
|
||||||
|
if (!$networkExists) {
|
||||||
|
$topLevelNetworks->put($networkDetails, null);
|
||||||
}
|
}
|
||||||
$savedService->fqdn = $defaultUsableFqdn;
|
|
||||||
$savedService->save();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$fqdns = data_get($savedService, 'fqdn');
|
|
||||||
if ($fqdns) {
|
// Collect/create/update ports
|
||||||
$fqdns = collect(Str::of($fqdns)->explode(','));
|
|
||||||
}
|
|
||||||
// Collect ports
|
|
||||||
$servicePorts = collect(data_get($service, 'ports', []));
|
|
||||||
$ports->put($serviceName, $servicePorts);
|
|
||||||
$collectedPorts = collect([]);
|
$collectedPorts = collect([]);
|
||||||
if ($servicePorts->count() > 0) {
|
if ($servicePorts->count() > 0) {
|
||||||
foreach ($servicePorts as $sport) {
|
foreach ($servicePorts as $sport) {
|
||||||
@@ -167,279 +224,286 @@ class Service extends BaseModel
|
|||||||
if (is_array($sport)) {
|
if (is_array($sport)) {
|
||||||
$target = data_get($sport, 'target');
|
$target = data_get($sport, 'target');
|
||||||
$published = data_get($sport, 'published');
|
$published = data_get($sport, 'published');
|
||||||
$collectedPorts->push("$target:$published");
|
$protocol = data_get($sport, 'protocol');
|
||||||
|
$collectedPorts->push("$target:$published/$protocol");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$savedService->ports = $collectedPorts->implode(',');
|
$savedService->ports = $collectedPorts->implode(',');
|
||||||
$savedService->save();
|
$savedService->save();
|
||||||
// Collect volumes
|
|
||||||
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
|
||||||
if ($serviceVolumes->count() > 0) {
|
|
||||||
LocalPersistentVolume::whereResourceId($savedService->id)->whereResourceType(get_class($savedService))->delete();
|
|
||||||
foreach ($serviceVolumes as $volume) {
|
|
||||||
if (is_string($volume) && Str::startsWith($volume, './')) {
|
|
||||||
// Local file
|
|
||||||
$fsPath = Str::before($volume, ':');
|
|
||||||
$volumePath = Str::of($volume)->after(':')->beforeLast(':');
|
|
||||||
ray($fsPath, $volumePath);
|
|
||||||
LocalFileVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'mount_path' => $volumePath,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService)
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'fs_path' => $fsPath,
|
|
||||||
'mount_path' => $volumePath,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_string($volume)) {
|
|
||||||
$volumeName = Str::before($volume, ':');
|
|
||||||
$volumePath = Str::after($volume, ':');
|
|
||||||
}
|
|
||||||
if (is_array($volume)) {
|
|
||||||
$volumeName = data_get($volume, 'source');
|
|
||||||
$volumePath = data_get($volume, 'target');
|
|
||||||
}
|
|
||||||
|
|
||||||
$volumeExists = $serviceVolumes->contains(function ($_, $key) use ($volumeName) {
|
|
||||||
return $key == $volumeName;
|
|
||||||
});
|
|
||||||
if (!$volumeExists) {
|
|
||||||
if (Str::startsWith($volumeName, '/')) {
|
|
||||||
$volumes->put($volumeName, $volumePath);
|
|
||||||
LocalPersistentVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'mount_path' => $volumePath,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService)
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => Str::slug($volumeName, '-'),
|
|
||||||
'mount_path' => $volumePath,
|
|
||||||
'host_path' => $volumeName,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$composeVolumes->put($volumeName, null);
|
|
||||||
LocalPersistentVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'name' => $volumeName,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService)
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => $volumeName,
|
|
||||||
'mount_path' => $volumePath,
|
|
||||||
'host_path' => null,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect and add networks
|
|
||||||
$serviceNetworks = collect(data_get($service, 'networks', []));
|
|
||||||
if ($serviceNetworks->count() > 0) {
|
|
||||||
foreach ($serviceNetworks as $networkName => $networkDetails) {
|
|
||||||
$networkExists = $composeNetworks->contains(function ($value, $key) use ($networkName) {
|
|
||||||
return $value == $networkName || $key == $networkName;
|
|
||||||
});
|
|
||||||
if (!$networkExists) {
|
|
||||||
$composeNetworks->put($networkDetails, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add Coolify specific networks
|
// Add Coolify specific networks
|
||||||
$definedNetworkExists = $composeNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||||
return $value == $definedNetwork;
|
return $value == $definedNetwork;
|
||||||
});
|
});
|
||||||
if (!$definedNetworkExists) {
|
if (!$definedNetworkExists) {
|
||||||
$composeNetworks->put($definedNetwork, [
|
$topLevelNetworks->put($definedNetwork, [
|
||||||
'name' => $definedNetwork,
|
'name' => $definedNetwork,
|
||||||
'external' => false
|
'external' => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$networks = $serviceNetworks->toArray();
|
$networks = $serviceNetworks->toArray();
|
||||||
$networks = array_merge($networks, [$definedNetwork]);
|
$networks = array_merge($networks, [$definedNetwork]);
|
||||||
data_set($service, 'networks', $networks);
|
data_set($service, 'networks', $networks);
|
||||||
|
|
||||||
|
// Collect/create/update volumes
|
||||||
|
if ($serviceVolumes->count() > 0) {
|
||||||
|
foreach ($serviceVolumes as $volume) {
|
||||||
|
$type = null;
|
||||||
|
$source = null;
|
||||||
|
$target = null;
|
||||||
|
$content = null;
|
||||||
|
$isDirectory = false;
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = Str::of($volume)->before(':');
|
||||||
|
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||||
|
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
|
||||||
|
$type = Str::of('bind');
|
||||||
|
} else {
|
||||||
|
$type = Str::of('volume');
|
||||||
|
}
|
||||||
|
} else if (is_array($volume)) {
|
||||||
|
$type = data_get_str($volume, 'type');
|
||||||
|
$source = data_get_str($volume, 'source');
|
||||||
|
$target = data_get_str($volume, 'target');
|
||||||
|
$content = data_get($volume, 'content');
|
||||||
|
$isDirectory = (bool) data_get($volume, 'isDirectory', false);
|
||||||
|
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
|
||||||
|
if ($foundConfig) {
|
||||||
|
$content = data_get($foundConfig, 'content');
|
||||||
|
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type->value() === 'bind') {
|
||||||
|
if ($source->value() === "/var/run/docker.sock") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LocalFileVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'fs_path' => $source,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'content' => $content,
|
||||||
|
'is_directory' => $isDirectory,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if ($type->value() === 'volume') {
|
||||||
|
$topLevelVolumes->put($source->value(), null);
|
||||||
|
LocalPersistentVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => Str::slug($source, '-'),
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$savedService->getFilesFromServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
|
||||||
// Get variables from the service
|
// Get variables from the service
|
||||||
foreach ($serviceVariables as $variable) {
|
foreach ($serviceVariables as $variableName => $variable) {
|
||||||
$value = Str::after($variable, '=');
|
if (is_numeric($variableName)) {
|
||||||
if (!Str::startsWith($value, '$SERVICE_') && !Str::startsWith($value, '${SERVICE_') && Str::startsWith($value, '$')) {
|
$variable = Str::of($variable);
|
||||||
$value = Str::of(replaceVariables(Str::of($value)));
|
if ($variable->contains('=')) {
|
||||||
if ($value->contains(':')) {
|
// - SESSION_SECRET=123
|
||||||
$nakedName = $value->before(':');
|
// - SESSION_SECRET=
|
||||||
$nakedValue = $value->after(':');
|
$key = $variable->before('=');
|
||||||
} else if ($value->contains('-')) {
|
$value = $variable->after('=');
|
||||||
$nakedName = $value->before('-');
|
|
||||||
$nakedValue = $value->after('-');
|
|
||||||
} else if ($value->contains('+')) {
|
|
||||||
$nakedName = $value->before('+');
|
|
||||||
$nakedValue = $value->after('+');
|
|
||||||
} else {
|
} else {
|
||||||
$nakedName = $value;
|
// - SESSION_SECRET
|
||||||
|
$key = $variable;
|
||||||
|
$value = null;
|
||||||
}
|
}
|
||||||
if (isset($nakedName)) {
|
} else {
|
||||||
if (isset($nakedValue)) {
|
// SESSION_SECRET: 123
|
||||||
if ($nakedValue->startsWith('-')) {
|
// SESSION_SECRET:
|
||||||
$nakedValue = Str::of($nakedValue)->after('-');
|
$key = Str::of($variableName);
|
||||||
|
$value = Str::of($variable);
|
||||||
|
}
|
||||||
|
if ($key->startsWith('SERVICE_FQDN')) {
|
||||||
|
if (is_null(data_get($savedService, 'fqdn'))) {
|
||||||
|
$fqdn = generateFqdn($this->server, $containerName);
|
||||||
|
if (substr_count($key->value(), '_') === 2 && $key->contains("=")) {
|
||||||
|
$path = $value->value();
|
||||||
|
if ($generatedServiceFQDNS->count() > 0) {
|
||||||
|
$alreadyGenerated = $generatedServiceFQDNS->has($key->value());
|
||||||
|
if ($alreadyGenerated) {
|
||||||
|
$fqdn = $generatedServiceFQDNS->get($key->value());
|
||||||
|
} else {
|
||||||
|
$generatedServiceFQDNS->put($key->value(), $fqdn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$generatedServiceFQDNS->put($key->value(), $fqdn);
|
||||||
}
|
}
|
||||||
if ($nakedValue->startsWith('+')) {
|
$fqdn = "$fqdn$path";
|
||||||
$nakedValue = Str::of($nakedValue)->after('+');
|
}
|
||||||
}
|
if (!$isDatabase) {
|
||||||
if (!$envs->has($nakedName->value())) {
|
$savedService->fqdn = $fqdn;
|
||||||
$envs->put($nakedName->value(), $nakedValue->value());
|
$savedService->save();
|
||||||
EnvironmentVariable::updateOrCreate([
|
}
|
||||||
'key' => $nakedName->value(),
|
}
|
||||||
'service_id' => $this->id,
|
continue;
|
||||||
], [
|
}
|
||||||
'value' => $nakedValue->value(),
|
if ($value?->startsWith('$')) {
|
||||||
|
$value = Str::of(replaceVariables($value));
|
||||||
|
$key = $value;
|
||||||
|
$foundEnv = EnvironmentVariable::where([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
])->first();
|
||||||
|
if ($value->startsWith('SERVICE_')) {
|
||||||
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
$forService = $value->afterLast('_');
|
||||||
|
$generatedValue = null;
|
||||||
|
if ($command->value() === 'FQDN' || $command->value() === 'URL') {
|
||||||
|
$fqdn = generateFqdn($this->server, $containerName);
|
||||||
|
if ($foundEnv) {
|
||||||
|
$fqdn = data_get($foundEnv, 'value');
|
||||||
|
} else {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $fqdn,
|
||||||
'is_build_time' => false,
|
'is_build_time' => false,
|
||||||
'service_id' => $this->id,
|
'service_id' => $this->id,
|
||||||
'is_preview' => false,
|
'is_preview' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$isDatabase) {
|
||||||
|
$savedService->fqdn = $fqdn;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!$envs->has($nakedName->value())) {
|
switch ($command) {
|
||||||
$envs->put($nakedName->value(), null);
|
case 'PASSWORD':
|
||||||
$envExists = EnvironmentVariable::where('service_id', $this->id)->where('key', $nakedName->value())->exists();
|
$generatedValue = Str::password(symbols: false);
|
||||||
if (!$envExists) {
|
break;
|
||||||
EnvironmentVariable::create([
|
case 'PASSWORD_64':
|
||||||
'key' => $nakedName->value(),
|
$generatedValue = Str::password(length: 64, symbols: false);
|
||||||
'value' => null,
|
break;
|
||||||
'service_id' => $this->id,
|
case 'BASE64_64':
|
||||||
'is_build_time' => false,
|
$generatedValue = Str::random(64);
|
||||||
'is_preview' => false,
|
break;
|
||||||
]);
|
case 'BASE64_128':
|
||||||
}
|
$generatedValue = Str::random(128);
|
||||||
|
break;
|
||||||
|
case 'BASE64':
|
||||||
|
$generatedValue = Str::random(32);
|
||||||
|
break;
|
||||||
|
case 'USER':
|
||||||
|
$generatedValue = Str::random(16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$foundEnv) {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $generatedValue,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if ($value->contains(':-')) {
|
||||||
$variableName = Str::of(replaceVariables(Str::of($value)));
|
$key = $value->before(':');
|
||||||
$generatedValue = null;
|
$defaultValue = $value->after(':-');
|
||||||
if ($variableName->startsWith('SERVICE_USER')) {
|
} else if ($value->contains('-')) {
|
||||||
$variableDefined = EnvironmentVariable::whereServiceId($this->id)->where('key', $variableName->value())->first();
|
$key = $value->before('-');
|
||||||
if (!$variableDefined) {
|
$defaultValue = $value->after('-');
|
||||||
$generatedValue = Str::random(10);
|
} else if ($value->contains(':?')) {
|
||||||
|
$key = $value->before(':');
|
||||||
|
$defaultValue = $value->after(':?');
|
||||||
|
} else if ($value->contains('?')) {
|
||||||
|
$key = $value->before('?');
|
||||||
|
$defaultValue = $value->after('?');
|
||||||
} else {
|
} else {
|
||||||
$generatedValue = $variableDefined->value;
|
$key = $value;
|
||||||
|
$defaultValue = null;
|
||||||
}
|
}
|
||||||
if (!$envs->has($variableName->value())) {
|
if ($foundEnv) {
|
||||||
$envs->put($variableName->value(), $generatedValue);
|
$defaultValue = data_get($foundEnv, 'value');
|
||||||
EnvironmentVariable::updateOrCreate([
|
|
||||||
'key' => $variableName->value(),
|
|
||||||
'service_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $generatedValue,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else if ($variableName->startsWith('SERVICE_PASSWORD')) {
|
|
||||||
$variableDefined = EnvironmentVariable::whereServiceId($this->id)->where('key', $variableName->value())->first();
|
|
||||||
if (!$variableDefined) {
|
|
||||||
$generatedValue = Str::password(symbols: false);
|
|
||||||
} else {
|
|
||||||
$generatedValue = $variableDefined->value;
|
|
||||||
}
|
|
||||||
if (!$envs->has($variableName->value())) {
|
|
||||||
$envs->put($variableName->value(), $generatedValue);
|
|
||||||
EnvironmentVariable::updateOrCreate([
|
|
||||||
'key' => $variableName->value(),
|
|
||||||
'service_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $generatedValue,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else if ($variableName->startsWith('SERVICE_FQDN')) {
|
|
||||||
if ($fqdns) {
|
|
||||||
$number = Str::of($variableName)->after('SERVICE_FQDN')->afterLast('_')->value();
|
|
||||||
if (is_numeric($number)) {
|
|
||||||
$number = (int) $number - 1;
|
|
||||||
} else {
|
|
||||||
$number = 0;
|
|
||||||
}
|
|
||||||
$fqdn = getOnlyFqdn(data_get($fqdns, $number, $fqdns->first()));
|
|
||||||
$environments = collect(data_get($service, 'environment'));
|
|
||||||
$environments = $environments->map(function ($envValue) use ($value, $fqdn) {
|
|
||||||
$envValue = Str::of($envValue)->replace($value, $fqdn);
|
|
||||||
return $envValue->value();
|
|
||||||
});
|
|
||||||
$service['environment'] = $environments->toArray();
|
|
||||||
}
|
|
||||||
} else if ($variableName->startsWith('SERVICE_URL')) {
|
|
||||||
if ($fqdns) {
|
|
||||||
$number = Str::of($variableName)->after('SERVICE_URL')->afterLast('_')->value();
|
|
||||||
if (is_numeric($number)) {
|
|
||||||
$number = (int) $number - 1;
|
|
||||||
} else {
|
|
||||||
$number = 0;
|
|
||||||
}
|
|
||||||
$fqdn = getOnlyFqdn(data_get($fqdns, $number, $fqdns->first()));
|
|
||||||
$url = Url::fromString($fqdn)->getHost();
|
|
||||||
$environments = collect(data_get($service, 'environment'));
|
|
||||||
$environments = $environments->map(function ($envValue) use ($value, $url) {
|
|
||||||
$envValue = Str::of($envValue)->replace($value, $url);
|
|
||||||
return $envValue->value();
|
|
||||||
});
|
|
||||||
$service['environment'] = $environments->toArray();
|
|
||||||
}
|
}
|
||||||
|
EnvironmentVariable::updateOrCreate([
|
||||||
|
'key' => $key,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
], [
|
||||||
|
'value' => $defaultValue,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->server->proxyType() === ProxyTypes::TRAEFIK_V2->value) {
|
|
||||||
$labels = collect(data_get($service, 'labels', []));
|
// Add labels to the service
|
||||||
$labels = collect([]);
|
$fqdns = collect(data_get($savedService, 'fqdns'));
|
||||||
$labels = $labels->merge(defaultLabels($this->id, $container_name, type: 'service'));
|
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
if (!$isDatabase) {
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if ($fqdns) {
|
if (!$isDatabase && $fqdns->count() > 0) {
|
||||||
$labels = $labels->merge(fqdnLabelsForTraefik($fqdns, $container_name, true));
|
if ($fqdns) {
|
||||||
}
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($fqdns, $containerName, true));
|
||||||
}
|
}
|
||||||
data_set($service, 'labels', $labels->toArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
data_forget($service, 'is_database');
|
data_forget($service, 'is_database');
|
||||||
data_set($service, 'restart', RESTART_MODE);
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
data_set($service, 'container_name', $container_name);
|
data_set($service, 'container_name', $containerName);
|
||||||
data_forget($service, 'documentation');
|
data_forget($service, 'volumes.*.content');
|
||||||
|
data_forget($service, 'volumes.*.isDirectory');
|
||||||
|
|
||||||
|
// Remove unnecessary variables from service.environment
|
||||||
|
$withoutServiceEnvs = collect([]);
|
||||||
|
collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {
|
||||||
|
if (!Str::of($key)->startsWith('$SERVICE_')) {
|
||||||
|
$withoutServiceEnvs->put($key, $value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data_set($service, 'environment', $withoutServiceEnvs->toArray());
|
||||||
return $service;
|
return $service;
|
||||||
});
|
});
|
||||||
$finalServices = [
|
$finalServices = [
|
||||||
'version' => $dockerComposeVersion,
|
'version' => $dockerComposeVersion,
|
||||||
'services' => $services->toArray(),
|
'services' => $services->toArray(),
|
||||||
'volumes' => $composeVolumes->toArray(),
|
'volumes' => $topLevelVolumes->toArray(),
|
||||||
'networks' => $composeNetworks->toArray(),
|
'networks' => $topLevelNetworks->toArray(),
|
||||||
];
|
];
|
||||||
|
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||||
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||||
$this->save();
|
$this->save();
|
||||||
$shouldBeDefined = collect([
|
$this->saveComposeConfigs();
|
||||||
'envs' => $envs,
|
return collect([]);
|
||||||
'volumes' => $volumes,
|
|
||||||
'ports' => $ports
|
|
||||||
]);
|
|
||||||
$parsedCompose = collect([
|
|
||||||
'dockerCompose' => $finalServices,
|
|
||||||
'shouldBeDefined' => $shouldBeDefined
|
|
||||||
]);
|
|
||||||
return $parsedCompose;
|
|
||||||
} else {
|
} else {
|
||||||
return collect([]);
|
return collect([]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
class ServiceApplication extends BaseModel
|
class ServiceApplication extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -15,10 +15,6 @@ class ServiceApplication extends BaseModel
|
|||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
public function documentation()
|
|
||||||
{
|
|
||||||
return data_get(Yaml::parse($this->service->docker_compose_raw), "services.{$this->name}.documentation", 'https://coolify.io/docs');
|
|
||||||
}
|
|
||||||
public function service()
|
public function service()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Service::class);
|
return $this->belongsTo(Service::class);
|
||||||
@@ -31,4 +27,17 @@ class ServiceApplication extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(LocalFileVolume::class, 'resource');
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
}
|
}
|
||||||
|
public function fqdns(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => is_null($this->fqdn)
|
||||||
|
? []
|
||||||
|
: explode(',', $this->fqdn),
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function getFilesFromServer()
|
||||||
|
{
|
||||||
|
getFilesystemVolumesFromServer($this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,6 @@ class ServiceDatabase extends BaseModel
|
|||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
public function documentation()
|
|
||||||
{
|
|
||||||
return data_get(Yaml::parse($this->service->docker_compose_raw), "services.{$this->name}.documentation", 'https://coolify.io/docs');
|
|
||||||
}
|
|
||||||
public function service()
|
public function service()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Service::class);
|
return $this->belongsTo(Service::class);
|
||||||
@@ -26,4 +22,12 @@ class ServiceDatabase extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||||
}
|
}
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
public function getFilesFromServer()
|
||||||
|
{
|
||||||
|
getFilesystemVolumesFromServer($this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class GeneralNotification extends Notification implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
public $tries = 5;
|
public $tries = 1;
|
||||||
public function __construct(public string $message)
|
public function __construct(public string $message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
|
||||||
use App\Notifications\Channels\EmailChannel;
|
|
||||||
use App\Notifications\Channels\TelegramChannel;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|||||||
@@ -50,5 +50,8 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
}
|
}
|
||||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||||
});
|
});
|
||||||
|
RateLimiter::for('5', function (Request $request) {
|
||||||
|
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Links extends Component
|
|||||||
if ($application->fqdn) {
|
if ($application->fqdn) {
|
||||||
$fqdns = collect(Str::of($application->fqdn)->explode(','));
|
$fqdns = collect(Str::of($application->fqdn)->explode(','));
|
||||||
$fqdns->map(function ($fqdn) {
|
$fqdns->map(function ($fqdn) {
|
||||||
$this->links->push(getOnlyFqdn($fqdn));
|
$this->links->push(getFqdnWithoutPort($fqdn));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($application->ports) {
|
if ($application->ports) {
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ const DATABASE_DOCKER_IMAGES = [
|
|||||||
'couchdb',
|
'couchdb',
|
||||||
'neo4j',
|
'neo4j',
|
||||||
'influxdb',
|
'influxdb',
|
||||||
'clickhouse'
|
'clickhouse/clickhouse-server'
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ function get_port_from_dockerfile($dockerfile): int
|
|||||||
return 80;
|
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 = collect([]);
|
||||||
$labels->push('coolify.managed=true');
|
$labels->push('coolify.managed=true');
|
||||||
@@ -141,13 +141,17 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
|||||||
if ($pull_request_id !== 0) {
|
if ($pull_request_id !== 0) {
|
||||||
$labels->push('coolify.pullRequestId=' . $pull_request_id);
|
$labels->push('coolify.pullRequestId=' . $pull_request_id);
|
||||||
}
|
}
|
||||||
|
if ($type === 'service') {
|
||||||
|
$labels->push('coolify.service.subId=' . $subId);
|
||||||
|
$labels->push('coolify.service.subType=' . $subType);
|
||||||
|
}
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function fqdnLabelsForTraefik(Collection $domains, $container_name, $is_force_https_enabled)
|
function fqdnLabelsForTraefik(Collection $domains, $container_name, $is_force_https_enabled)
|
||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels->push('traefik.enable=true');
|
$labels->push('traefik.enable=true');
|
||||||
foreach($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
$url = Url::fromString($domain);
|
$url = Url::fromString($domain);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
@@ -216,9 +220,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
} else {
|
} else {
|
||||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
}
|
}
|
||||||
if ($application->destination->server->proxy->type === ProxyTypes::TRAEFIK_V2->value) {
|
// Add Traefik labels no matter which proxy is selected
|
||||||
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $container_name, $application->settings->is_force_https_enabled));
|
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $container_name, $application->settings->is_force_https_enabled));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Actions\Proxy\SaveConfiguration;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
@@ -10,7 +11,8 @@ function get_proxy_path()
|
|||||||
$proxy_path = "$base_path/proxy";
|
$proxy_path = "$base_path/proxy";
|
||||||
return $proxy_path;
|
return $proxy_path;
|
||||||
}
|
}
|
||||||
function connectProxyToNetworks(Server $server) {
|
function connectProxyToNetworks(Server $server)
|
||||||
|
{
|
||||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||||
return $docker['network'];
|
return $docker['network'];
|
||||||
})->unique();
|
})->unique();
|
||||||
@@ -20,7 +22,7 @@ function connectProxyToNetworks(Server $server) {
|
|||||||
$commands = $networks->map(function ($network) {
|
$commands = $networks->map(function ($network) {
|
||||||
return [
|
return [
|
||||||
"echo '####### Connecting coolify-proxy to $network network...'",
|
"echo '####### Connecting coolify-proxy to $network network...'",
|
||||||
"docker network ls --format '{{.Name}}' | grep '^$network$' || docker network create --attachable $network >/dev/null",
|
"docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null || docker network create --attachable $network >/dev/null",
|
||||||
"docker network connect $network coolify-proxy >/dev/null 2>&1 || true",
|
"docker network connect $network coolify-proxy >/dev/null 2>&1 || true",
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
@@ -101,7 +103,9 @@ function generate_default_proxy_configuration(Server $server)
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$config['services']['traefik']['command'][] = "--log.level=debug";
|
$config['services']['traefik']['command'][] = "--log.level=debug";
|
||||||
}
|
}
|
||||||
return Yaml::dump($config, 4, 2);
|
$config = Yaml::dump($config, 4, 2);
|
||||||
|
SaveConfiguration::run($server, $config);
|
||||||
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ function validateServer(Server $server, bool $throwError = false)
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
$server->settings->is_reachable = true;
|
$server->settings->is_reachable = true;
|
||||||
|
instant_remote_process(["docker ps"], $server, $throwError);
|
||||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
||||||
if (!$dockerVersion) {
|
if (!$dockerVersion) {
|
||||||
$dockerVersion = null;
|
$dockerVersion = null;
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
function replaceRegex(?string $name = null)
|
function replaceRegex(?string $name = null)
|
||||||
{
|
{
|
||||||
@@ -20,27 +25,115 @@ function serviceStatus(Service $service)
|
|||||||
{
|
{
|
||||||
$foundRunning = false;
|
$foundRunning = false;
|
||||||
$isDegraded = false;
|
$isDegraded = false;
|
||||||
|
$foundRestaring = false;
|
||||||
$applications = $service->applications;
|
$applications = $service->applications;
|
||||||
$databases = $service->databases;
|
$databases = $service->databases;
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
|
if ($application->exclude_from_status) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (Str::of($application->status)->startsWith('running')) {
|
if (Str::of($application->status)->startsWith('running')) {
|
||||||
$foundRunning = true;
|
$foundRunning = true;
|
||||||
|
} else if (Str::of($application->status)->startsWith('restarting')) {
|
||||||
|
$foundRestaring = true;
|
||||||
} else {
|
} else {
|
||||||
$isDegraded = true;
|
$isDegraded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($databases as $database) {
|
foreach ($databases as $database) {
|
||||||
|
if ($database->exclude_from_status) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (Str::of($database->status)->startsWith('running')) {
|
if (Str::of($database->status)->startsWith('running')) {
|
||||||
$foundRunning = true;
|
$foundRunning = true;
|
||||||
|
} else if (Str::of($database->status)->startsWith('restarting')) {
|
||||||
|
$foundRestaring = true;
|
||||||
} else {
|
} else {
|
||||||
$isDegraded = true;
|
$isDegraded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($foundRestaring) {
|
||||||
|
return 'degraded';
|
||||||
|
}
|
||||||
if ($foundRunning && !$isDegraded) {
|
if ($foundRunning && !$isDegraded) {
|
||||||
return 'running';
|
return 'running';
|
||||||
} else if ($foundRunning && $isDegraded) {
|
} else if ($foundRunning && $isDegraded) {
|
||||||
return 'degraded';
|
return 'degraded';
|
||||||
} else if (!$foundRunning && $isDegraded) {
|
} else if (!$foundRunning && !$isDegraded) {
|
||||||
return 'exited';
|
return 'exited';
|
||||||
}
|
}
|
||||||
|
return 'exited';
|
||||||
|
}
|
||||||
|
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneService)
|
||||||
|
{
|
||||||
|
// TODO: make this async
|
||||||
|
try {
|
||||||
|
$workdir = $oneService->service->workdir();
|
||||||
|
$server = $oneService->service->server;
|
||||||
|
$fileVolumes = $oneService->fileStorages()->get();
|
||||||
|
$commands = collect([
|
||||||
|
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||||
|
"cd "
|
||||||
|
]);
|
||||||
|
instant_remote_process($commands, $server);
|
||||||
|
foreach ($fileVolumes as $fileVolume) {
|
||||||
|
$path = Str::of(data_get($fileVolume, 'fs_path'));
|
||||||
|
$content = data_get($fileVolume, 'content');
|
||||||
|
if ($path->startsWith('.')) {
|
||||||
|
$path = $path->after('.');
|
||||||
|
$fileLocation = $workdir . $path;
|
||||||
|
} else {
|
||||||
|
$fileLocation = $path;
|
||||||
|
}
|
||||||
|
$isFile = instant_remote_process(["test -f $fileLocation && echo OK || echo NOK"], $server);
|
||||||
|
$isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server);
|
||||||
|
if ($isFile == 'OK' && !$fileVolume->is_directory) {
|
||||||
|
$filesystemContent = instant_remote_process(["cat $fileLocation"], $server);
|
||||||
|
if (base64_encode($filesystemContent) != base64_encode($content)) {
|
||||||
|
$fileVolume->content = $filesystemContent;
|
||||||
|
$fileVolume->save();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($isDir == 'OK') {
|
||||||
|
$fileVolume->content = null;
|
||||||
|
$fileVolume->is_directory = true;
|
||||||
|
$fileVolume->save();
|
||||||
|
} else {
|
||||||
|
$fileVolume->content = null;
|
||||||
|
$fileVolume->is_directory = false;
|
||||||
|
$fileVolume->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateCompose($resource)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$name = data_get($resource, 'name');
|
||||||
|
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
|
||||||
|
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
||||||
|
|
||||||
|
// Switch Image
|
||||||
|
$image = data_get($resource, 'image');
|
||||||
|
data_set($dockerCompose, "services.{$name}.image", $image);
|
||||||
|
|
||||||
|
// Update FQDN
|
||||||
|
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper();
|
||||||
|
ray($variableName);
|
||||||
|
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||||
|
if ($generatedEnv) {
|
||||||
|
$generatedEnv->value = $resource->fqdn;
|
||||||
|
$generatedEnv->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
|
||||||
|
$resource->service->docker_compose_raw = $dockerComposeRaw;
|
||||||
|
$resource->service->save();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
@@ -11,11 +12,13 @@ use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
|||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Support\Stringable;
|
||||||
use Nubs\RandomNameGenerator\All;
|
use Nubs\RandomNameGenerator\All;
|
||||||
use Poliander\Cron\CronExpression;
|
use Poliander\Cron\CronExpression;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
@@ -240,11 +243,13 @@ function base_ip(): string
|
|||||||
}
|
}
|
||||||
return "localhost";
|
return "localhost";
|
||||||
}
|
}
|
||||||
function getOnlyFqdn(String $fqdn) {
|
function getFqdnWithoutPort(String $fqdn)
|
||||||
|
{
|
||||||
$url = Url::fromString($fqdn);
|
$url = Url::fromString($fqdn);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$scheme = $url->getScheme();
|
$scheme = $url->getScheme();
|
||||||
return "$scheme://$host";
|
$path = $url->getPath();
|
||||||
|
return "$scheme://$host$path";
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* If fqdn is set, return it, otherwise return public ip.
|
* If fqdn is set, return it, otherwise return public ip.
|
||||||
@@ -383,3 +388,49 @@ function parseEnvFormatToArray($env_file_contents)
|
|||||||
}
|
}
|
||||||
return $env_array;
|
return $env_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function data_get_str($data, $key, $default = null): Stringable
|
||||||
|
{
|
||||||
|
$str = data_get($data, $key, $default) ?? $default;
|
||||||
|
return Str::of($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFqdn(Server $server, string $random)
|
||||||
|
{
|
||||||
|
$wildcard = data_get($server, 'settings.wildcard_domain');
|
||||||
|
if (is_null($wildcard) || $wildcard === '') {
|
||||||
|
$wildcard = sslip($server);
|
||||||
|
}
|
||||||
|
$url = Url::fromString($wildcard);
|
||||||
|
$host = $url->getHost();
|
||||||
|
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
||||||
|
$scheme = $url->getScheme();
|
||||||
|
$finalFqdn = "$scheme://{$random}.$host$path" ;
|
||||||
|
return $finalFqdn;
|
||||||
|
}
|
||||||
|
function sslip(Server $server)
|
||||||
|
{
|
||||||
|
if (isDev()) {
|
||||||
|
return "http://127.0.0.1.sslip.io";
|
||||||
|
}
|
||||||
|
if ($server->ip === 'host.docker.internal') {
|
||||||
|
$baseIp = base_ip();
|
||||||
|
return "http://$baseIp.sslip.io";
|
||||||
|
}
|
||||||
|
return "http://{$server->ip}.sslip.io";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServiceTemplates()
|
||||||
|
{
|
||||||
|
if (isDev()) {
|
||||||
|
$services = File::get(base_path('templates/service-templates.json'));
|
||||||
|
$services = collect(json_decode($services))->sortKeys();
|
||||||
|
} else {
|
||||||
|
$services = Http::get(config('constants.services.official'));
|
||||||
|
if ($services->failed()) {
|
||||||
|
throw new \Exception($services->body());
|
||||||
|
}
|
||||||
|
$services = collect($services->json())->sortKeys();
|
||||||
|
}
|
||||||
|
return $services;
|
||||||
|
}
|
||||||
|
|||||||
725
composer.lock
generated
725
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'ssh' =>[
|
'docs' => [
|
||||||
|
'base_url' => 'https://coolify.io/docs',
|
||||||
|
'contact' => 'https://coolify.io/docs/contact',
|
||||||
|
],
|
||||||
|
'ssh' => [
|
||||||
'connection_timeout' => 10,
|
'connection_timeout' => 10,
|
||||||
'server_interval' => 20,
|
'server_interval' => 20,
|
||||||
'command_timeout' => 7200,
|
'command_timeout' => 7200,
|
||||||
@@ -14,8 +18,11 @@ return [
|
|||||||
'expiration' => 10,
|
'expiration' => 10,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'services' => [
|
||||||
|
'official' => 'https://cdn.coollabs.io/coolify/service-templates.json',
|
||||||
|
],
|
||||||
'limits' => [
|
'limits' => [
|
||||||
'trial_period'=> 7,
|
'trial_period' => 7,
|
||||||
'server' => [
|
'server' => [
|
||||||
'zero' => 0,
|
'zero' => 0,
|
||||||
'self-hosted' => 999999999999,
|
'self-hosted' => 999999999999,
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
return [
|
return [
|
||||||
|
|
||||||
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
|
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
|
||||||
'dsn' => 'https://62de992090e4e0cb28f18231835ea006@o1082494.ingest.sentry.io/4505347448045568',
|
'dsn' => 'https://72f02655749d5d687297b6b9f078b8b9@o1082494.ingest.sentry.io/4505347448045568',
|
||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.44',
|
'release' => '4.0.0-beta.52',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ return [
|
|||||||
*
|
*
|
||||||
* Minimum: 3000 (in milliseconds)
|
* Minimum: 3000 (in milliseconds)
|
||||||
*/
|
*/
|
||||||
'duration' => 3000,
|
'duration' => 5000,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The horizontal position of each toast.
|
* The horizontal position of each toast.
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.44';
|
return '4.0.0-beta.52';
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('destination_type');
|
||||||
|
$table->dropColumn('destination_id');
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->morphs('destination');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?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('exclude_from_status')->default(false);
|
||||||
|
$table->boolean('required_fqdn')->default(false);
|
||||||
|
$table->string('image')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('exclude_from_status');
|
||||||
|
$table->dropColumn('required_fqdn');
|
||||||
|
$table->dropColumn('image');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?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('exclude_from_status')->default(false);
|
||||||
|
$table->string('image')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_databases', function (Blueprint $table) {
|
||||||
|
$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('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('marketing_emails')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('marketing_emails');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -69,11 +69,12 @@ class ProductionSeeder extends Seeder
|
|||||||
PrivateKey::updateOrCreate(
|
PrivateKey::updateOrCreate(
|
||||||
[
|
[
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => 'localhost\'s key',
|
|
||||||
'description' => 'The private key for the Coolify host machine (localhost).',
|
|
||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
],
|
],
|
||||||
['private_key' => $coolify_key]
|
[
|
||||||
|
'name' => 'localhost\'s key',
|
||||||
|
'description' => 'The private key for the Coolify host machine (localhost).', 'private_key' => $coolify_key
|
||||||
|
]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
echo "No SSH key found for the Coolify host machine (localhost).\n";
|
echo "No SSH key found for the Coolify host machine (localhost).\n";
|
||||||
|
|||||||
27
examples/docker-compose-fider.yaml
Normal file
27
examples/docker-compose-fider.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
fider:
|
||||||
|
image: getfider/fider:stable
|
||||||
|
environment:
|
||||||
|
BASE_URL: $SERVICE_FQDN_FIDER
|
||||||
|
DATABASE_URL: postgres://$SERVICE_USER_MYSQL:$SERVICE_PASSWORD_MYSQL@database:5432/fider?sslmode=disable
|
||||||
|
JWT_SECRET: $SERVICE_PASSWORD64_FIDER
|
||||||
|
EMAIL_NOREPLY: ${EMAIL_NOREPLY:-noreply@example.com}
|
||||||
|
EMAIL_MAILGUN_API: $EMAIL_MAILGUN_API
|
||||||
|
EMAIL_MAILGUN_DOMAIN: $EMAIL_MAILGUN_DOMAIN
|
||||||
|
EMAIL_MAILGUN_REGION: $EMAIL_MAILGUN_REGION
|
||||||
|
EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST:-smtp.mailgun.com}
|
||||||
|
EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT:-587}
|
||||||
|
EMAIL_SMTP_USERNAME: ${EMAIL_SMTP_USERNAME:-postmaster@mailgun.com}
|
||||||
|
EMAIL_SMTP_PASSWORD: $EMAIL_SMTP_PASSWORD
|
||||||
|
EMAIL_SMTP_ENABLE_STARTTLS: $EMAIL_SMTP_ENABLE_STARTTLS
|
||||||
|
EMAIL_AWSSES_REGION: $EMAIL_AWSSES_REGION
|
||||||
|
EMAIL_AWSSES_ACCESS_KEY_ID: $EMAIL_AWSSES_ACCESS_KEY_ID
|
||||||
|
EMAIL_AWSSES_SECRET_ACCESS_KEY: $EMAIL_AWSSES_SECRET_ACCESS_KEY
|
||||||
|
database:
|
||||||
|
image: postgres:12
|
||||||
|
volumes:
|
||||||
|
- pg_data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: $SERVICE_USER_MYSQL
|
||||||
|
POSTGRES_PASSWORD: $SERVICE_PASSWORD_MYSQL
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-fider}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
services:
|
services:
|
||||||
ghost:
|
ghost:
|
||||||
documentation: https://ghost.org/docs/config
|
|
||||||
image: ghost:5
|
image: ghost:5
|
||||||
volumes:
|
volumes:
|
||||||
- ghost-content-data:/var/lib/ghost/content
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
- type: volume
|
|
||||||
source: /data/g
|
|
||||||
target: /data
|
|
||||||
volume:
|
|
||||||
nocopy: true
|
|
||||||
environment:
|
environment:
|
||||||
- url=$SERVICE_FQDN_GHOST
|
- url=$SERVICE_FQDN_GHOST
|
||||||
- database__client=mysql
|
- database__client=mysql
|
||||||
@@ -16,24 +10,9 @@ services:
|
|||||||
- database__connection__user=$SERVICE_USER_MYSQL
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
- database__connection__database=${MYSQL_DATABASE-ghost}
|
- database__connection__database=${MYSQL_DATABASE-ghost}
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
aliases:
|
|
||||||
- alias1
|
|
||||||
- alias3
|
|
||||||
ipv4_address: 172.16.238.10
|
|
||||||
ipv6_address: 2001:3984:3989::10
|
|
||||||
ports:
|
|
||||||
- "2368"
|
|
||||||
- 1234:2368
|
|
||||||
- target: 2368
|
|
||||||
published: 1234
|
|
||||||
protocol: tcp
|
|
||||||
mode: host
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
mysql:
|
mysql:
|
||||||
documentation: https://hub.docker.com/_/mysql
|
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
volumes:
|
volumes:
|
||||||
- ghost-mysql-data:/var/lib/mysql
|
- ghost-mysql-data:/var/lib/mysql
|
||||||
@@ -42,10 +21,3 @@ services:
|
|||||||
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
ipam:
|
|
||||||
driver: default
|
|
||||||
config:
|
|
||||||
- subnet: "172.16.238.0/24"
|
|
||||||
- subnet: "2001:3984:3989::/64"
|
|
||||||
|
|||||||
52
examples/docker-compose-plausible.yaml
Normal file
52
examples/docker-compose-plausible.yaml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
plausible:
|
||||||
|
image: plausible/analytics:v2.0
|
||||||
|
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgres://postgres:$SERVICE_PASSWORD_POSTGRES@plausible_db/plausible
|
||||||
|
- BASE_URL=$SERVICE_FQDN_PLAUSIBLE
|
||||||
|
- SECRET_KEY_BASE=$SERVICE_BASE64_64_PLAUSIBLE
|
||||||
|
depends_on:
|
||||||
|
- plausible_db
|
||||||
|
- plausible_events_db
|
||||||
|
- mail
|
||||||
|
|
||||||
|
mail:
|
||||||
|
image: bytemark/smtp
|
||||||
|
|
||||||
|
plausible_db:
|
||||||
|
image: postgres:14-alpine
|
||||||
|
volumes:
|
||||||
|
- db-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=plausible
|
||||||
|
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||||
|
|
||||||
|
plausible_events_db:
|
||||||
|
image: clickhouse/clickhouse-server:23.3.7.5-alpine
|
||||||
|
volumes:
|
||||||
|
- type: volume
|
||||||
|
source: event-data
|
||||||
|
target: /var/lib/clickhouse
|
||||||
|
- type: bind
|
||||||
|
source: ./clickhouse/clickhouse-config.xml
|
||||||
|
target: /etc/clickhouse-server/config.d/logging.xml
|
||||||
|
read_only: true
|
||||||
|
content: >-
|
||||||
|
<clickhouse><profiles><default><log_queries>0</log_queries><log_query_threads>0</log_query_threads></default></profiles></clickhouse>
|
||||||
|
- type: bind
|
||||||
|
source: ./clickhouse/clickhouse-user-config.xml
|
||||||
|
target: /etc/clickhouse-server/users.d/logging.xml
|
||||||
|
read_only: true
|
||||||
|
content: >-
|
||||||
|
<clickhouse><logger><level>warning</level><console>true</console></logger><query_thread_log
|
||||||
|
remove="remove"/><query_log remove="remove"/><text_log
|
||||||
|
remove="remove"/><trace_log remove="remove"/><metric_log
|
||||||
|
remove="remove"/><asynchronous_metric_log
|
||||||
|
remove="remove"/><session_log remove="remove"/><part_log
|
||||||
|
remove="remove"/></clickhouse>
|
||||||
|
ulimits:
|
||||||
|
nofile:
|
||||||
|
soft: 262144
|
||||||
|
hard: 262144
|
||||||
15
examples/docker-compose-postgres.yaml
Normal file
15
examples/docker-compose-postgres.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres
|
||||||
|
command: 'postgres -c config_file=/etc/postgresql/postgresql.conf'
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ./postgresql.conf
|
||||||
|
target: /etc/postgresql/postgresql.conf
|
||||||
|
- type: bind
|
||||||
|
source: ./docker-entrypoint-initdb.d
|
||||||
|
target: /docker-entrypoint-initdb.d/
|
||||||
|
isDirectory: true
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: $SERVICE_USER_POSTGRES
|
||||||
|
POSTGRES_PASSWORD: $SERVICE_PASSWORD_POSTGRES
|
||||||
5
examples/docker-compose-uptime-kuma.yaml
Normal file
5
examples/docker-compose-uptime-kuma.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
services:
|
||||||
|
uptime-kuma:
|
||||||
|
image: louislam/uptime-kuma:1
|
||||||
|
volumes:
|
||||||
|
- uptime-kuma:/app/data
|
||||||
76
examples/docker-compose-weird.yaml
Normal file
76
examples/docker-compose-weird.yaml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:5
|
||||||
|
volumes:
|
||||||
|
- ~/configs:/etc/configs/:ro
|
||||||
|
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
|
||||||
|
- /var/lib/ghost/content:/tmp/ghost/content:rw
|
||||||
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
|
- type: volume
|
||||||
|
source: mydata
|
||||||
|
target: /data
|
||||||
|
volume:
|
||||||
|
nocopy: true
|
||||||
|
- type: bind
|
||||||
|
source: ./var/lib/ghost/data
|
||||||
|
target: /data
|
||||||
|
- type: bind
|
||||||
|
source: /tmp
|
||||||
|
target: /tmp
|
||||||
|
labels:
|
||||||
|
- "test.label=true"
|
||||||
|
ports:
|
||||||
|
- "3000"
|
||||||
|
- "3000-3005"
|
||||||
|
- "8000:8000"
|
||||||
|
- "9090-9091:8080-8081"
|
||||||
|
- "49100:22"
|
||||||
|
- "127.0.0.1:8001:8001"
|
||||||
|
- "127.0.0.1:5000-5010:5000-5010"
|
||||||
|
- "127.0.0.1::5000"
|
||||||
|
- "6060:6060/udp"
|
||||||
|
- "12400-12500:1240"
|
||||||
|
- target: 80
|
||||||
|
published: 8080
|
||||||
|
protocol: tcp
|
||||||
|
mode: host
|
||||||
|
networks:
|
||||||
|
- some-network
|
||||||
|
- other-network
|
||||||
|
environment:
|
||||||
|
- database__client=${DATABASE_CLIENT:-mysql}
|
||||||
|
- database__connection__database=${MYSQL_DATABASE:-ghost}
|
||||||
|
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
|
||||||
|
- test=${TEST:?true}
|
||||||
|
- url=$SERVICE_FQDN_GHOST
|
||||||
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
volumes:
|
||||||
|
- ghost-mysql-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=$MYSQL_DATABASE
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||||
|
- SESSION_SECRET
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
environment:
|
||||||
|
RACK_ENV: development
|
||||||
|
A: $A
|
||||||
|
SHOW: ${SHOW}
|
||||||
|
SHOW1: ${SHOW2-show1}
|
||||||
|
SHOW2: ${SHOW3:-show2}
|
||||||
|
SHOW3: ${SHOW4?show3}
|
||||||
|
SHOW4: ${SHOW5:?show4}
|
||||||
|
SHOW5: ${SERVICE_USER_MINIO}
|
||||||
|
SHOW6: ${SERVICE_PASSWORD_MINIO}
|
||||||
|
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
|
||||||
|
SHOW8: ${SERVICE_BASE64_64_MINIO}
|
||||||
|
SHOW9: ${SERVICE_BASE64_128_MINIO}
|
||||||
|
SHOW10: ${SERVICE_BASE64_MINIO}
|
||||||
|
SHOW11:
|
||||||
352
package-lock.json
generated
352
package-lock.json
generated
@@ -5,19 +5,19 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.9",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.12.2",
|
"alpinejs": "3.13.0",
|
||||||
"daisyui": "3.2.1",
|
"daisyui": "3.7.7",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.2.3",
|
"@vitejs/plugin-vue": "4.3.4",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.4.0",
|
"axios": "1.5.0",
|
||||||
"laravel-vite-plugin": "0.7.8",
|
"laravel-vite-plugin": "0.8.0",
|
||||||
"postcss": "8.4.24",
|
"postcss": "8.4.30",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.3",
|
||||||
"vite": "4.3.9",
|
"vite": "4.4.9",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -45,9 +45,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
|
||||||
"integrity": "sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==",
|
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==",
|
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -77,9 +77,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==",
|
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -93,9 +93,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==",
|
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -109,9 +109,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==",
|
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -125,9 +125,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==",
|
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -141,9 +141,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==",
|
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -157,9 +157,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
|
||||||
"integrity": "sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==",
|
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -173,9 +173,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==",
|
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -189,9 +189,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
|
||||||
"integrity": "sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==",
|
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -205,9 +205,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
|
||||||
"integrity": "sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==",
|
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -221,9 +221,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
|
||||||
"integrity": "sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==",
|
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@@ -237,9 +237,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
|
||||||
"integrity": "sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==",
|
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -253,9 +253,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
|
||||||
"integrity": "sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==",
|
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -269,9 +269,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
|
||||||
"integrity": "sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==",
|
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -285,9 +285,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==",
|
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -301,9 +301,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==",
|
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -317,9 +317,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==",
|
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -333,9 +333,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==",
|
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -349,9 +349,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
|
||||||
"integrity": "sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==",
|
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -365,9 +365,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
|
||||||
"integrity": "sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==",
|
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -381,9 +381,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
|
||||||
"integrity": "sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==",
|
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -477,9 +477,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/typography": {
|
"node_modules/@tailwindcss/typography": {
|
||||||
"version": "0.5.9",
|
"version": "0.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
|
||||||
"integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==",
|
"integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.castarray": "^4.4.0",
|
"lodash.castarray": "^4.4.0",
|
||||||
"lodash.isplainobject": "^4.0.6",
|
"lodash.isplainobject": "^4.0.6",
|
||||||
@@ -503,9 +503,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.2.3",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
|
||||||
"integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==",
|
"integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
@@ -683,9 +683,9 @@
|
|||||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
||||||
},
|
},
|
||||||
"node_modules/alpinejs": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.12.2",
|
"version": "3.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz",
|
||||||
"integrity": "sha512-wrUZULm9w6DYwWcUtB/anFLgRaF4riSuPgIJ3gcPUS8st9luAJnAxoIgro/Al97d2McSSz/JypWg/NlmKFIJJA==",
|
"integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "~3.1.1"
|
"@vue/reactivity": "~3.1.1"
|
||||||
}
|
}
|
||||||
@@ -719,9 +719,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.14",
|
"version": "10.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz",
|
||||||
"integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
|
"integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -731,12 +731,16 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserslist": "^4.21.5",
|
"browserslist": "^4.21.10",
|
||||||
"caniuse-lite": "^1.0.30001464",
|
"caniuse-lite": "^1.0.30001538",
|
||||||
"fraction.js": "^4.2.0",
|
"fraction.js": "^4.3.6",
|
||||||
"normalize-range": "^0.1.2",
|
"normalize-range": "^0.1.2",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
@@ -752,9 +756,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.4.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
|
||||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.0",
|
"follow-redirects": "^1.15.0",
|
||||||
@@ -796,9 +800,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.21.5",
|
"version": "4.21.11",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz",
|
||||||
"integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
|
"integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -808,13 +812,17 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001449",
|
"caniuse-lite": "^1.0.30001538",
|
||||||
"electron-to-chromium": "^1.4.284",
|
"electron-to-chromium": "^1.4.526",
|
||||||
"node-releases": "^2.0.8",
|
"node-releases": "^2.0.13",
|
||||||
"update-browserslist-db": "^1.0.10"
|
"update-browserslist-db": "^1.0.13"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@@ -832,9 +840,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001467",
|
"version": "1.0.30001539",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001467.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001539.tgz",
|
||||||
"integrity": "sha512-cEdN/5e+RPikvl9AHm4uuLXxeCNq8rFsQ+lPHTfe/OtypP3WwnVVbjn+6uBV7PaFL6xUFzTh+sSCOz1rKhcO+Q==",
|
"integrity": "sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -844,6 +852,10 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -941,9 +953,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "3.2.1",
|
"version": "3.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.7.7.tgz",
|
||||||
"integrity": "sha512-gIqE6wiqoJt9G8+n3R/SwLeUnpNCE2eDhT73rP0yZYVaM7o6zVcakBH3aEW5RGpx3UkonPiLuvcgxRcb2lE8TA==",
|
"integrity": "sha512-2/nFdW/6R9MMnR8tTm07jPVyPaZwpUSkVsFAADb7Oq8N2Ynbls57laDdNqxTCUmn0QvcZi01TKl8zQbAwRfw1w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colord": "^2.9",
|
"colord": "^2.9",
|
||||||
"css-selector-tokenizer": "^0.8",
|
"css-selector-tokenizer": "^0.8",
|
||||||
@@ -979,15 +991,15 @@
|
|||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.332",
|
"version": "1.4.528",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.332.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.528.tgz",
|
||||||
"integrity": "sha512-c1Vbv5tuUlBFp0mb3mCIjw+REEsgthRgNE8BlbEDKmvzb8rxjcVki6OkQP83vLN34s0XCxpSkq7AZNep1a6xhw==",
|
"integrity": "sha512-UdREXMXzLkREF4jA8t89FQjA8WHI6ssP38PMY4/4KhXFQbtImnghh4GkCgrtiZwLKUKVD2iTVXvDVQjfomEQuA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.17.12",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
|
||||||
"integrity": "sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==",
|
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -997,28 +1009,28 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/android-arm": "0.17.12",
|
"@esbuild/android-arm": "0.18.20",
|
||||||
"@esbuild/android-arm64": "0.17.12",
|
"@esbuild/android-arm64": "0.18.20",
|
||||||
"@esbuild/android-x64": "0.17.12",
|
"@esbuild/android-x64": "0.18.20",
|
||||||
"@esbuild/darwin-arm64": "0.17.12",
|
"@esbuild/darwin-arm64": "0.18.20",
|
||||||
"@esbuild/darwin-x64": "0.17.12",
|
"@esbuild/darwin-x64": "0.18.20",
|
||||||
"@esbuild/freebsd-arm64": "0.17.12",
|
"@esbuild/freebsd-arm64": "0.18.20",
|
||||||
"@esbuild/freebsd-x64": "0.17.12",
|
"@esbuild/freebsd-x64": "0.18.20",
|
||||||
"@esbuild/linux-arm": "0.17.12",
|
"@esbuild/linux-arm": "0.18.20",
|
||||||
"@esbuild/linux-arm64": "0.17.12",
|
"@esbuild/linux-arm64": "0.18.20",
|
||||||
"@esbuild/linux-ia32": "0.17.12",
|
"@esbuild/linux-ia32": "0.18.20",
|
||||||
"@esbuild/linux-loong64": "0.17.12",
|
"@esbuild/linux-loong64": "0.18.20",
|
||||||
"@esbuild/linux-mips64el": "0.17.12",
|
"@esbuild/linux-mips64el": "0.18.20",
|
||||||
"@esbuild/linux-ppc64": "0.17.12",
|
"@esbuild/linux-ppc64": "0.18.20",
|
||||||
"@esbuild/linux-riscv64": "0.17.12",
|
"@esbuild/linux-riscv64": "0.18.20",
|
||||||
"@esbuild/linux-s390x": "0.17.12",
|
"@esbuild/linux-s390x": "0.18.20",
|
||||||
"@esbuild/linux-x64": "0.17.12",
|
"@esbuild/linux-x64": "0.18.20",
|
||||||
"@esbuild/netbsd-x64": "0.17.12",
|
"@esbuild/netbsd-x64": "0.18.20",
|
||||||
"@esbuild/openbsd-x64": "0.17.12",
|
"@esbuild/openbsd-x64": "0.18.20",
|
||||||
"@esbuild/sunos-x64": "0.17.12",
|
"@esbuild/sunos-x64": "0.18.20",
|
||||||
"@esbuild/win32-arm64": "0.17.12",
|
"@esbuild/win32-arm64": "0.18.20",
|
||||||
"@esbuild/win32-ia32": "0.17.12",
|
"@esbuild/win32-ia32": "0.18.20",
|
||||||
"@esbuild/win32-x64": "0.17.12"
|
"@esbuild/win32-x64": "0.18.20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
@@ -1121,16 +1133,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fraction.js": {
|
"node_modules/fraction.js": {
|
||||||
"version": "4.2.0",
|
"version": "4.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
|
||||||
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
|
"integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "patreon",
|
"type": "patreon",
|
||||||
"url": "https://www.patreon.com/infusion"
|
"url": "https://github.com/sponsors/rawify"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
@@ -1277,9 +1289,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/laravel-vite-plugin": {
|
"node_modules/laravel-vite-plugin": {
|
||||||
"version": "0.7.8",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.8.tgz",
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.0.tgz",
|
||||||
"integrity": "sha512-HWYqpQYHR3kEQ1LsHX7gHJoNNf0bz5z5mDaHBLzS+PGLCTmYqlU5/SZyeEgObV7z7bC/cnStYcY9H1DI1D5Udg==",
|
"integrity": "sha512-6VjLI+azBpeK6rWBiKcb/En5GnTdYpL0U4zS8gXYvb2/VSq4mlau5H3NWpSktUDBMM1b97LLgICx5zevi8IY0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
@@ -1412,9 +1424,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.10",
|
"version": "2.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||||
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
|
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
@@ -1504,9 +1516,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.24",
|
"version": "8.4.30",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
|
||||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1697,9 +1709,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.21.0",
|
"version": "3.29.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.3.tgz",
|
||||||
"integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==",
|
"integrity": "sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@@ -1794,9 +1806,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz",
|
||||||
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
|
"integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
@@ -1818,7 +1830,6 @@
|
|||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"postcss-nested": "^6.0.1",
|
"postcss-nested": "^6.0.1",
|
||||||
"postcss-selector-parser": "^6.0.11",
|
"postcss-selector-parser": "^6.0.11",
|
||||||
"postcss-value-parser": "^4.2.0",
|
|
||||||
"resolve": "^1.22.2",
|
"resolve": "^1.22.2",
|
||||||
"sucrase": "^3.32.0"
|
"sucrase": "^3.32.0"
|
||||||
},
|
},
|
||||||
@@ -1874,9 +1885,9 @@
|
|||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||||
},
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1886,6 +1897,10 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1893,7 +1908,7 @@
|
|||||||
"picocolors": "^1.0.0"
|
"picocolors": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist-lint": "cli.js"
|
"update-browserslist-db": "cli.js"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
@@ -1905,14 +1920,14 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.3.9",
|
"version": "4.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
|
||||||
"integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
|
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.17.5",
|
"esbuild": "^0.18.10",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.27",
|
||||||
"rollup": "^3.21.0"
|
"rollup": "^3.27.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@@ -1920,12 +1935,16 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
},
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": ">= 14",
|
"@types/node": ">= 14",
|
||||||
"less": "*",
|
"less": "*",
|
||||||
|
"lightningcss": "^1.21.0",
|
||||||
"sass": "*",
|
"sass": "*",
|
||||||
"stylus": "*",
|
"stylus": "*",
|
||||||
"sugarss": "*",
|
"sugarss": "*",
|
||||||
@@ -1938,6 +1957,9 @@
|
|||||||
"less": {
|
"less": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"sass": {
|
"sass": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
|||||||
20
package.json
20
package.json
@@ -6,19 +6,19 @@
|
|||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.2.3",
|
"@vitejs/plugin-vue": "4.3.4",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.4.0",
|
"axios": "1.5.0",
|
||||||
"laravel-vite-plugin": "0.7.8",
|
"laravel-vite-plugin": "0.8.0",
|
||||||
"postcss": "8.4.24",
|
"postcss": "8.4.30",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.3",
|
||||||
"vite": "4.3.9",
|
"vite": "4.4.9",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.9",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.12.2",
|
"alpinejs": "3.13.0",
|
||||||
"daisyui": "3.2.1",
|
"daisyui": "3.7.7",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
public/vendor/horizon/app.js
vendored
2
public/vendor/horizon/app.js
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/horizon/mix-manifest.json
vendored
2
public/vendor/horizon/mix-manifest.json
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"/app.js": "/app.js?id=7e1968acfd75b8dc843675097962e3ce",
|
"/app.js": "/app.js?id=ff1533ec4a7afad65c5bd7bcc2cc7d7b",
|
||||||
"/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
|
"/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
|
||||||
"/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
|
"/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
|
||||||
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
|
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<div>
|
<div >
|
||||||
<div class="flex items-center p-1 px-2 mt-1 overflow-hidden transition-all transform rounded cursor-pointer bg-coolgray-200"
|
<div class="flex items-center p-1 px-2 overflow-hidden transition-all transform rounded cursor-pointer bg-coolgray-200"
|
||||||
@click="showCommandPalette = true">
|
@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"
|
<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">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|||||||
@@ -15,14 +15,13 @@
|
|||||||
@if (is_transactional_emails_active())
|
@if (is_transactional_emails_active())
|
||||||
<form action="/forgot-password" method="POST" class="flex flex-col gap-2">
|
<form action="/forgot-password" method="POST" class="flex flex-col gap-2">
|
||||||
@csrf
|
@csrf
|
||||||
<x-forms.input required type="email" name="email"
|
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" autofocus />
|
||||||
label="{{ __('input.email') }}" autofocus />
|
|
||||||
<x-forms.button type="submit">{{ __('auth.forgot_password_send_email') }}</x-forms.button>
|
<x-forms.button type="submit">{{ __('auth.forgot_password_send_email') }}</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
@else
|
@else
|
||||||
<div>Transactional emails are not active on this instance.</div>
|
<div>Transactional emails are not active on this instance.</div>
|
||||||
<div>See how to set it in our <a class="text-white" target="_blank"
|
<div>See how to set it in our <a class="text-white" target="_blank"
|
||||||
href="https://docs.coollabs.io/coolify">docs</a>, or how to
|
href="{{ config('constants.docs.base_url') }}">docs</a>, or how to
|
||||||
manually reset password.
|
manually reset password.
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<x-chevron-down />
|
<x-chevron-down />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="absolute hidden group-hover:block">
|
<div class="absolute z-50 hidden group-hover:block">
|
||||||
<ul tabindex="0" class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200">
|
<ul tabindex="0" class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200">
|
||||||
@if (data_get($application, 'gitBrancLocation'))
|
@if (data_get($application, 'gitBrancLocation'))
|
||||||
<li>
|
<li>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
@foreach (Str::of(data_get($application, 'fqdn'))->explode(',') as $fqdn)
|
@foreach (Str::of(data_get($application, 'fqdn'))->explode(',') as $fqdn)
|
||||||
<li>
|
<li>
|
||||||
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
|
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
|
||||||
target="_blank" href="{{ getOnlyFqdn($fqdn) }}">
|
target="_blank" href="{{ getFqdnWithoutPort($fqdn) }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
stroke-linejoin="round">
|
stroke-linejoin="round">
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
|
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
|
||||||
<path
|
<path
|
||||||
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
|
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
|
||||||
</svg>{{ getOnlyFqdn($fqdn) }}
|
</svg>{{ getFqdnWithoutPort($fqdn) }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
@if (data_get($preview, 'fqdn'))
|
@if (data_get($preview, 'fqdn'))
|
||||||
<li>
|
<li>
|
||||||
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
|
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
|
||||||
target="_blank" href="{{ getOnlyFqdn(data_get($preview, 'fqdn')) }}">
|
target="_blank" href="{{ getFqdnWithoutPort(data_get($preview, 'fqdn')) }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
stroke-linejoin="round">
|
stroke-linejoin="round">
|
||||||
|
|||||||
12
resources/views/components/collapsible.blade.php
Normal file
12
resources/views/components/collapsible.blade.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@isset($title, $action)
|
||||||
|
<div tabindex="0" x-data="{ open: false }"
|
||||||
|
class="transition border rounded cursor-pointer collapse collapse-arrow border-coolgray-200"
|
||||||
|
:class="open ? 'collapse-open' : 'collapse-close'">
|
||||||
|
<div class="flex flex-col justify-center text-sm select-text collapse-title" x-on:click="open = !open">
|
||||||
|
{{ $title }}
|
||||||
|
</div>
|
||||||
|
<div class="collapse-content">
|
||||||
|
{{ $action }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endisset
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@auth
|
@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">
|
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
|
||||||
<li title="Dashboard">
|
<li title="Dashboard">
|
||||||
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
|
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
|
||||||
@@ -10,16 +11,13 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="Help" class="mt-auto">
|
<li title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs">
|
||||||
<div class="justify-center icons" wire:click="help" onclick="help.showModal()">
|
<div class="justify-center" wire:click="help" onclick="help.showModal()">
|
||||||
<svg class="{{ request()->is('help*') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24"
|
<svg class="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
<path fill="currentColor"
|
||||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
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" />
|
||||||
stroke-width="2">
|
|
||||||
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0-18 0m9 4v.01" />
|
|
||||||
<path d="M12 13a2 2 0 0 0 .914-3.782a1.98 1.98 0 0 0-2.414.483" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Feedback
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="pb-6" title="Logout">
|
<li class="pb-6" title="Logout">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@auth
|
@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">
|
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
|
||||||
<li title="Dashboard">
|
<li title="Dashboard">
|
||||||
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
|
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
|
||||||
@@ -114,16 +115,13 @@
|
|||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
@if (isSubscriptionActive() || isDev())
|
@if (isSubscriptionActive() || isDev())
|
||||||
<li title="Help" class="mt-auto">
|
<li title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs">
|
||||||
<div class="justify-center icons" wire:click="help" onclick="help.showModal()">
|
<div class="justify-center" wire:click="help" onclick="help.showModal()">
|
||||||
<svg class="{{ request()->is('help*') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24"
|
<svg class="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
<path fill="currentColor"
|
||||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
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" />
|
||||||
stroke-width="2">
|
|
||||||
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0-18 0m9 4v.01" />
|
|
||||||
<path d="M12 13a2 2 0 0 0 .914-3.782a1.98 1.98 0 0 0-2.414.483" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Feedback
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
<div class="group">
|
@if ($links->count() > 0)
|
||||||
<label tabindex="0" class="flex items-center gap-2 cursor-pointer hover:text-white"> Open Application
|
<div class="group">
|
||||||
<x-chevron-down />
|
<label tabindex="0" class="flex items-center gap-2 cursor-pointer hover:text-white"> Open Application
|
||||||
</label>
|
<x-chevron-down />
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="absolute hidden group-hover:block">
|
<div class="absolute z-50 hidden group-hover:block">
|
||||||
<ul tabindex="0" class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200">
|
<ul tabindex="0"
|
||||||
@if ($links->count() > 0)
|
class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200">
|
||||||
@foreach ($links as $link)
|
@if ($links->count() > 0)
|
||||||
<li>
|
@foreach ($links as $link)
|
||||||
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
|
<li>
|
||||||
target="_blank" href="{{ $link }}">
|
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
target="_blank" href="{{ $link }}">
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
||||||
stroke-linejoin="round">
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
stroke-linejoin="round">
|
||||||
<path d="M9 15l6 -6" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
|
<path d="M9 15l6 -6" />
|
||||||
<path
|
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
|
||||||
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
|
<path
|
||||||
</svg>{{ $link }}
|
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
|
||||||
</a>
|
</svg>{{ $link }}
|
||||||
</li>
|
</a>
|
||||||
@endforeach
|
</li>
|
||||||
@endif
|
@endforeach
|
||||||
</ul>
|
@endif
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endif
|
||||||
|
|||||||
@@ -1,27 +1,7 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<x-services.links :service="$service" />
|
<x-services.links :service="$service" />
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
@if (serviceStatus($service) === 'running')
|
@if (serviceStatus($service) === 'degraded')
|
||||||
<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')
|
|
||||||
<button wire:click='deploy' onclick="startService.showModal()"
|
<button wire:click='deploy' onclick="startService.showModal()"
|
||||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
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">
|
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -42,4 +22,34 @@
|
|||||||
Stop
|
Stop
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@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>
|
</div>
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-20 hover:opacity-100 hover:text-white']) }}
|
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-20 hover:opacity-100 hover:text-white z-50']) }}
|
||||||
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}">v{{ config('version') }}</a>
|
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}">v{{ config('version') }}</a>
|
||||||
|
|||||||
5
resources/views/emails/updates.blade.php
Normal file
5
resources/views/emails/updates.blade.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<x-emails.layout>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
If you do not like to receive these emails, you can unsubscribe [here]({{$unsubscribeUrl}}).
|
||||||
|
</x-emails.layout>
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
<p class="mt-6 text-base leading-7 text-neutral-300">There has been an error, we are working on it.
|
<p class="mt-6 text-base leading-7 text-neutral-300">There has been an error, we are working on it.
|
||||||
</p>
|
</p>
|
||||||
@if ($exception->getMessage() !== '')
|
@if ($exception->getMessage() !== '')
|
||||||
<p class="mt-6 text-xs leading-7 text-left text-red-500">Error: {{ $exception->getMessage() }}
|
<code class="mt-6 text-xs text-left text-red-500">Error: {{ $exception->getMessage() }}
|
||||||
</p>
|
</code>
|
||||||
@endif
|
@endif
|
||||||
<div class="flex items-center justify-center mt-10 gap-x-6">
|
<div class="flex items-center justify-center mt-10 gap-x-6">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@section('body')
|
@section('body')
|
||||||
@parent
|
@parent
|
||||||
<x-navbar />
|
<x-navbar />
|
||||||
<div class="fixed top-3 left-4" id="vue">
|
<div class="fixed z-50 top-[4.5rem] left-4" id="vue">
|
||||||
<magic-bar></magic-bar>
|
<magic-bar></magic-bar>
|
||||||
</div>
|
</div>
|
||||||
<main class="main max-w-screen-2xl">
|
<main class="main max-w-screen-2xl">
|
||||||
|
|||||||
@@ -94,6 +94,7 @@
|
|||||||
})
|
})
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyToClipboard(text) {
|
function copyToClipboard(text) {
|
||||||
navigator.clipboard.writeText(text);
|
navigator.clipboard.writeText(text);
|
||||||
Livewire.emit('success', 'Copied to clipboard.');
|
Livewire.emit('success', 'Copied to clipboard.');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@section('body')
|
@section('body')
|
||||||
@parent
|
@parent
|
||||||
@if (isSubscriptionOnGracePeriod())
|
@if (isSubscriptionOnGracePeriod())
|
||||||
<div class="fixed top-3 left-4" id="vue">
|
<div class="fixed top-[4.5rem] left-4 z-50" id="vue">
|
||||||
<magic-bar></magic-bar>
|
<magic-bar></magic-bar>
|
||||||
</div>
|
</div>
|
||||||
<x-navbar />
|
<x-navbar />
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div
|
<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>
|
<pre class="font-mono whitespace-pre-wrap" @if ($isPollingActive) wire:poll.2000ms="polling" @endif>{{ RunRemoteProcess::decodeOutput($this->activity) }}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<x-boarding-step title="Server">
|
<x-boarding-step title="Server">
|
||||||
<x-slot:question>
|
<x-slot:question>
|
||||||
Do you want to deploy your resources on your <x-highlighted text="Localhost" />
|
Do you want to deploy your resources on your <x-highlighted text="Localhost" />
|
||||||
or on a <x-highlighted text="Remote Server" />?
|
or on a<x-highlighted text="Remote Server" />?
|
||||||
</x-slot:question>
|
</x-slot:question>
|
||||||
<x-slot:actions>
|
<x-slot:actions>
|
||||||
<x-forms.button class="justify-center box" wire:target="setServerType('localhost')"
|
<x-forms.button class="justify-center box" wire:target="setServerType('localhost')"
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<div class="flex flex-col gap-2 rounded modal-box">
|
<div class="flex flex-col w-11/12 max-w-5xl gap-2 modal-box">
|
||||||
<h3>How can we help?</h3>
|
<h3>How can we help?</h3>
|
||||||
<div>You can report bug about the current page (details will be included automatically), or send us general feedback.</div>
|
<div>Your feedback helps us to improve Coolify. Thank you! 💜</div>
|
||||||
<form wire:submit.prevent="submit" class="flex flex-col gap-4 pt-4">
|
<form wire:submit.prevent="submit" class="flex flex-col gap-4 pt-4">
|
||||||
<x-forms.input id="subject" label="Subject" placeholder="Summary of your problem."></x-forms.input>
|
<x-forms.input id="subject" label="Subject" placeholder="Summary of your problem."></x-forms.input>
|
||||||
<x-forms.textarea id="description" label="Message"
|
<x-forms.textarea rows="10" id="description" label="Description"
|
||||||
placeholder="Please provide as much information as possible."></x-forms.textarea>
|
placeholder="Please provide as much information as possible."></x-forms.textarea>
|
||||||
<x-forms.button class="w-full mt-4" type="submit">Send Request</x-forms.button>
|
<div></div>
|
||||||
|
<x-forms.button class="w-full mt-4" type="submit" onclick="help.close()">Send Email</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,17 +15,17 @@
|
|||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
<x-forms.input placeholder="https://coolify.io" id="application.fqdn" label="Domains"
|
<x-forms.input placeholder="https://coolify.io" id="application.fqdn" label="Domains"
|
||||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " />
|
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " />
|
||||||
@if ($wildcard_domain)
|
<x-forms.button wire:click="getWildcardDomain">Generate Domain
|
||||||
@if ($global_wildcard_domain)
|
</x-forms.button>
|
||||||
<x-forms.button wire:click="generateGlobalRandomDomain">Set Global Wildcard
|
|
||||||
</x-forms.button>
|
|
||||||
@endif
|
|
||||||
@if ($server_wildcard_domain)
|
|
||||||
<x-forms.button wire:click="generateServerRandomDomain">Set Server Wildcard
|
|
||||||
</x-forms.button>
|
|
||||||
@endif
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
@if (!$application->dockerfile)
|
||||||
|
<div class="flex items-end gap-2">
|
||||||
|
<x-forms.select id="application.build_pack" label="Build Pack" required>
|
||||||
|
<option value="nixpacks">Nixpacks</option>
|
||||||
|
<option value="dockerfile">Dockerfile</option>
|
||||||
|
</x-forms.select>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@if ($application->settings->is_static)
|
@if ($application->settings->is_static)
|
||||||
<x-forms.select id="application.static_image" label="Static Image" required>
|
<x-forms.select id="application.static_image" label="Static Image" required>
|
||||||
<option value="nginx:alpine">nginx:alpine</option>
|
<option value="nginx:alpine">nginx:alpine</option>
|
||||||
@@ -54,6 +54,7 @@
|
|||||||
@if ($application->dockerfile)
|
@if ($application->dockerfile)
|
||||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<h3>Network</h3>
|
<h3>Network</h3>
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
@if ($application->settings->is_static)
|
@if ($application->settings->is_static)
|
||||||
@@ -63,7 +64,7 @@
|
|||||||
helper="A comma separated list of ports your application uses. The first port will be used as default healthcheck port if nothing defined in the Healthcheck menu. Be sure to set this correctly." />
|
helper="A comma separated list of ports your application uses. The first port will be used as default healthcheck port if nothing defined in the Healthcheck menu. Be sure to set this correctly." />
|
||||||
@endif
|
@endif
|
||||||
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
||||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><span class='inline-block font-bold text-warning'>Example</span>3000:3000,3002:3002" />
|
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3>Advanced</h3>
|
<h3>Advanced</h3>
|
||||||
@@ -80,7 +81,7 @@
|
|||||||
id="is_auto_deploy_enabled" label="Auto Deploy" />
|
id="is_auto_deploy_enabled" label="Auto Deploy" />
|
||||||
<x-forms.checkbox
|
<x-forms.checkbox
|
||||||
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
||||||
instantSave id="is_preview_deployments_enabled" label="Previews Deployments" />
|
instantSave id="is_preview_deployments_enabled" label="Preview Deployments" />
|
||||||
|
|
||||||
<x-forms.checkbox instantSave id="is_git_submodules_enabled" label="Git Submodules"
|
<x-forms.checkbox instantSave id="is_git_submodules_enabled" label="Git Submodules"
|
||||||
helper="Allow Git Submodules during build process." />
|
helper="Allow Git Submodules during build process." />
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user