mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-01 05:09:23 +00:00
Compare commits
164 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ad8ca224f | ||
|
|
050e56f69a | ||
|
|
6099ac11d9 | ||
|
|
adac728a60 | ||
|
|
e2e64e36a0 | ||
|
|
762af66cbf | ||
|
|
b08f525bd4 | ||
|
|
5ae16b195c | ||
|
|
91db1953ff | ||
|
|
1c8f92d3b7 | ||
|
|
4075572dbc | ||
|
|
2971e360d7 | ||
|
|
32bb2780f2 | ||
|
|
af69575b29 | ||
|
|
d4a7d0d25f | ||
|
|
45f9def0f6 | ||
|
|
5a90eed7ef | ||
|
|
38e1f17edf | ||
|
|
1651845e20 | ||
|
|
38e96548b5 | ||
|
|
47e4126dca | ||
|
|
e0b175ab07 | ||
|
|
93ec785f4f | ||
|
|
e849addab8 | ||
|
|
a5e6975dac | ||
|
|
4ac8e1cc67 | ||
|
|
1a5e3a7836 | ||
|
|
4498d1ed4b | ||
|
|
c8b974820b | ||
|
|
527373e297 | ||
|
|
a84be8dc33 | ||
|
|
bd856f7f67 | ||
|
|
8ff216e5fb | ||
|
|
5255311a2e | ||
|
|
774a245e84 | ||
|
|
194675c838 | ||
|
|
75862ca8de | ||
|
|
5580a4e704 | ||
|
|
51e601a303 | ||
|
|
734e9fd68d | ||
|
|
09fc950ae8 | ||
|
|
cf6caa279d | ||
|
|
68c976ab70 | ||
|
|
1560ab2a50 | ||
|
|
e3a6458506 | ||
|
|
1768b9374f | ||
|
|
51d0a30a6c | ||
|
|
9701c65297 | ||
|
|
58e3bb2571 | ||
|
|
31cbd1602d | ||
|
|
dd5723d596 | ||
|
|
620f26a6f1 | ||
|
|
540717e809 | ||
|
|
d446cd4103 | ||
|
|
7d1a76570c | ||
|
|
e18766ec21 | ||
|
|
3d0354cf7e | ||
|
|
af5b9fced1 | ||
|
|
ab5202515e | ||
|
|
f863db7ea5 | ||
|
|
3adc0bdd6e | ||
|
|
97027875bf | ||
|
|
fd9c13009f | ||
|
|
46a72fac47 | ||
|
|
5d6ee04991 | ||
|
|
d523becb29 | ||
|
|
41672f75d0 | ||
|
|
acd8541e68 | ||
|
|
ed6af777a4 | ||
|
|
05f162f4e8 | ||
|
|
5e0adc3777 | ||
|
|
7d06fc4403 | ||
|
|
0e1bcceb8e | ||
|
|
a922f2fedf | ||
|
|
1e0226c8ed | ||
|
|
5c45908087 | ||
|
|
aefdc76805 | ||
|
|
b3c8c881b7 | ||
|
|
9ab5a1f7bd | ||
|
|
23968e7886 | ||
|
|
390d24b6d7 | ||
|
|
e4296345b3 | ||
|
|
bcffbe418b | ||
|
|
bfbee4e78f | ||
|
|
2bdb44dac3 | ||
|
|
4daa1b8c16 | ||
|
|
febc399568 | ||
|
|
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 |
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:
|
||||||
|
|||||||
28
CONTRIBUTION.md
Normal file
28
CONTRIBUTION.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
> "First, thanks for considering to contribute to my project.
|
||||||
|
It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
||||||
|
|
||||||
|
You can ask for guidance anytime on our
|
||||||
|
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
||||||
|
|
||||||
|
|
||||||
|
## 1) Setup your development environment
|
||||||
|
|
||||||
|
- You need to have Docker Engine (or equivalent) [installed](https://docs.docker.com/engine/install/) on your system.
|
||||||
|
- For better DX, install [Spin](https://serversideup.net/open-source/spin/).
|
||||||
|
|
||||||
|
## 2) Set your environment variables
|
||||||
|
|
||||||
|
- Copy [.env.development.example](./.env.development.example) to .env.
|
||||||
|
- If necessary, set `USERID` & `GROUPID` accordingly (read in .env file).
|
||||||
|
|
||||||
|
## 3) Start & setup Coolify
|
||||||
|
|
||||||
|
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||||
|
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
||||||
|
|
||||||
|
## 4) Start development
|
||||||
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
|
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,29 +4,30 @@ namespace App\Actions\Service;
|
|||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartService
|
class StartService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
$workdir = service_configuration_dir() . "/{$service->uuid}";
|
$network = $service->destination->network;
|
||||||
|
$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";
|
||||||
|
$compose = data_get($service,'docker_compose',[]);
|
||||||
|
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
||||||
|
foreach($serviceNames as $serviceName => $serviceConfig){
|
||||||
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} 2>/dev/null || true";
|
||||||
|
}
|
||||||
$activity = remote_process($commands, $service->server);
|
$activity = remote_process($commands, $service->server);
|
||||||
return $activity;
|
return $activity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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', [
|
||||||
|
|||||||
108
app/Console/Commands/ResourcesDelete.php
Normal file
108
app/Console/Commands/ResourcesDelete.php
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
use function Laravel\Prompts\multiselect;
|
||||||
|
use function Laravel\Prompts\select;
|
||||||
|
|
||||||
|
class ResourcesDelete extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'resources:delete';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Delete a resource from the database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$resource = select(
|
||||||
|
'What resource do you want to delete?',
|
||||||
|
['Application', 'Database', 'Service'],
|
||||||
|
);
|
||||||
|
if ($resource === 'Application') {
|
||||||
|
$this->deleteApplication();
|
||||||
|
} elseif ($resource === 'Database') {
|
||||||
|
$this->deleteDatabase();
|
||||||
|
} elseif ($resource === 'Service') {
|
||||||
|
$this->deleteService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteApplication()
|
||||||
|
{
|
||||||
|
$applications = Application::all();
|
||||||
|
if ($applications->count() === 0) {
|
||||||
|
$this->error('There are no applications to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$applicationsToDelete = multiselect(
|
||||||
|
'What application do you want to delete?',
|
||||||
|
$applications->pluck('name')->toArray(),
|
||||||
|
);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($applicationsToDelete as $application) {
|
||||||
|
$toDelete = $applications->where('name', $application)->first();
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteDatabase()
|
||||||
|
{
|
||||||
|
$databases = StandalonePostgresql::all();
|
||||||
|
if ($databases->count() === 0) {
|
||||||
|
$this->error('There are no databases to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$databasesToDelete = multiselect(
|
||||||
|
'What database do you want to delete?',
|
||||||
|
$databases->pluck('name')->toArray(),
|
||||||
|
);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($databasesToDelete as $database) {
|
||||||
|
$toDelete = $databases->where('name', $database)->first();
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private function deleteService()
|
||||||
|
{
|
||||||
|
$services = Service::all();
|
||||||
|
if ($services->count() === 0) {
|
||||||
|
$this->error('There are no services to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$servicesToDelete = multiselect(
|
||||||
|
'What service do you want to delete?',
|
||||||
|
$services->pluck('name')->toArray(),
|
||||||
|
);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($servicesToDelete as $service) {
|
||||||
|
$toDelete = $services->where('name', $service)->first();
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
app/Console/Commands/UsersResetRoot.php
Normal file
49
app/Console/Commands/UsersResetRoot.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\password;
|
||||||
|
|
||||||
|
class UsersResetRoot extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'users:reset-root';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Reset Root Password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->info('You are about to reset the root password.');
|
||||||
|
$password = password('Give me a new password for root user: ');
|
||||||
|
$passwordAgain = password('Again');
|
||||||
|
if ($password != $passwordAgain) {
|
||||||
|
$this->error('Passwords do not match.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->info('Updating root password...');
|
||||||
|
try {
|
||||||
|
User::find(0)->update(['password' => Hash::make($password)]);
|
||||||
|
$this->info('Root password updated successfully.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to update root password.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 App\Models\StandaloneDocker;
|
||||||
|
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,73 @@ 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");
|
||||||
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
|
if ($oneClickDotEnvs) {
|
||||||
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||||
|
}
|
||||||
|
if ($oneClickService) {
|
||||||
|
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||||
|
$service = Service::create([
|
||||||
|
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||||
|
'docker_compose_raw' => base64_decode($oneClickService),
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'server_id' => (int) $server_id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
|
]);
|
||||||
|
$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()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
Team::find(currentTeam()->id)->update([
|
Team::find(currentTeam()->id)->update([
|
||||||
'show_boarding' => false
|
'show_boarding' => false
|
||||||
]);
|
]);
|
||||||
ray(currentTeam());
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@@ -129,6 +128,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()
|
||||||
|
|||||||
28
app/Http/Livewire/Dev/Compose.php
Normal file
28
app/Http/Livewire/Dev/Compose.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Dev;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Compose extends Component
|
||||||
|
{
|
||||||
|
public string $compose = '';
|
||||||
|
public string $base64 = '';
|
||||||
|
public $services;
|
||||||
|
public function mount() {
|
||||||
|
$this->services = getServiceTemplates();
|
||||||
|
}
|
||||||
|
public function setService(string $selected) {
|
||||||
|
$this->base64 = data_get($this->services, $selected . '.compose');
|
||||||
|
if ($this->base64) {
|
||||||
|
$this->compose = base64_decode($this->base64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function updatedCompose($value) {
|
||||||
|
$this->base64 = base64_encode($value);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.dev.compose');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,52 +88,31 @@ 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;
|
if (data_get($this->application,'settings')) {
|
||||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
$this->is_static = $this->application->settings->is_static;
|
||||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
$this->checkWildCardDomain();
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
@@ -151,7 +127,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
if (data_get($this->application, 'dockerfile')) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port) {
|
if ($port && !$this->application->ports_exposes) {
|
||||||
$this->application->ports_exposes = $port;
|
$this->application->ports_exposes = $port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$container_name = generateApplicationContainerName($this->application);
|
$container_name = generateApplicationContainerName($this->application, $pull_request_id);
|
||||||
ray('Stopping container: ' . $container_name);
|
|
||||||
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ class General extends Component
|
|||||||
$this->getDbUrl();
|
$this->getDbUrl();
|
||||||
}
|
}
|
||||||
public function getDbUrl() {
|
public function getDbUrl() {
|
||||||
|
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->ip}:{$this->database->public_port}/{$this->database->postgres_db}";
|
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->getIp}:{$this->database->public_port}/{$this->database->postgres_db}";
|
||||||
} else {
|
} else {
|
||||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}";
|
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,73 +2,140 @@
|
|||||||
|
|
||||||
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
|
- type: bind
|
||||||
- database__connection__database=${MYSQL_DATABASE-ghost}
|
source: ./var/lib/ghost/data
|
||||||
depends_on:
|
target: /data
|
||||||
- mysql
|
- type: bind
|
||||||
mysql:
|
source: /tmp
|
||||||
documentation: https://hub.docker.com/_/mysql
|
target: /tmp
|
||||||
image: mysql:8.0
|
labels:
|
||||||
volumes:
|
- "test.label=true"
|
||||||
- ghost-mysql-data:/var/lib/mysql
|
ports:
|
||||||
environment:
|
- "3000"
|
||||||
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
- "3000-3005"
|
||||||
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
- "8000:8000"
|
||||||
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
- "9090-9091:8080-8081"
|
||||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
|
- "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();
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ class PublicGitRepository extends Component
|
|||||||
public string $git_branch = 'main';
|
public string $git_branch = 'main';
|
||||||
public int $rate_limit_remaining = 0;
|
public int $rate_limit_remaining = 0;
|
||||||
public $rate_limit_reset = 0;
|
public $rate_limit_reset = 0;
|
||||||
|
private object $repository_url_parsed;
|
||||||
|
public GithubApp|GitlabApp|null $git_source = null;
|
||||||
|
public string $git_host;
|
||||||
|
public string $git_repository;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required|url',
|
'repository_url' => 'required|url',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
@@ -38,10 +42,6 @@ class PublicGitRepository extends Component
|
|||||||
'is_static' => 'static',
|
'is_static' => 'static',
|
||||||
'publish_directory' => 'publish directory',
|
'publish_directory' => 'publish directory',
|
||||||
];
|
];
|
||||||
private object $repository_url_parsed;
|
|
||||||
private GithubApp|GitlabApp|null $git_source = null;
|
|
||||||
private string $git_host;
|
|
||||||
private string $git_repository;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -76,13 +76,15 @@ 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);
|
ray($e->getMessage());
|
||||||
}
|
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
try {
|
||||||
try {
|
$this->git_branch = 'master';
|
||||||
$this->git_branch = 'master';
|
$this->get_branch();
|
||||||
$this->get_branch();
|
} catch (\Throwable $e) {
|
||||||
} catch (\Throwable $e) {
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,9 +124,6 @@ class PublicGitRepository extends Component
|
|||||||
$project_uuid = $this->parameters['project_uuid'];
|
$project_uuid = $this->parameters['project_uuid'];
|
||||||
$environment_name = $this->parameters['environment_name'];
|
$environment_name = $this->parameters['environment_name'];
|
||||||
|
|
||||||
$this->get_git_source();
|
|
||||||
$this->git_branch = $this->selected_branch ?? $this->git_branch;
|
|
||||||
|
|
||||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
if (!$destination) {
|
if (!$destination) {
|
||||||
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||||
@@ -156,10 +155,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
|
||||||
$port = get_port_from_dockerfile($this->dockerfile);
|
$port = get_port_from_dockerfile($this->dockerfile);
|
||||||
|
if (!$port) {
|
||||||
|
$port = 80;
|
||||||
|
}
|
||||||
$application = Application::create([
|
$application = Application::create([
|
||||||
'name' => 'dockerfile-' . new Cuid2(7),
|
'name' => 'dockerfile-' . new Cuid2(7),
|
||||||
'repository_project_id' => 0,
|
'repository_project_id' => 0,
|
||||||
@@ -56,14 +59,12 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => $destination->id,
|
'destination_id' => $destination->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
'source_id' => 0,
|
'source_id' => 0,
|
||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$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,72 @@
|
|||||||
|
|
||||||
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->applications = $this->service->applications->sort();
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class Navbar extends Component
|
|||||||
}
|
}
|
||||||
public function serviceStatusUpdated()
|
public function serviceStatusUpdated()
|
||||||
{
|
{
|
||||||
ray('serviceStatusUpdated');
|
|
||||||
$this->check_status();
|
$this->check_status();
|
||||||
}
|
}
|
||||||
public function check_status()
|
public function check_status()
|
||||||
@@ -39,5 +38,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,23 @@ 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();
|
||||||
|
}
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public function generateDockerCompose()
|
public function generateDockerCompose()
|
||||||
{
|
{
|
||||||
|
|||||||
35
app/Http/Livewire/Project/Service/Storage.php
Normal file
35
app/Http/Livewire/Project/Service/Storage.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\LocalPersistentVolume;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Storage extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['addNewVolume'];
|
||||||
|
public $resource;
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.storage');
|
||||||
|
}
|
||||||
|
public function addNewVolume($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
LocalPersistentVolume::create([
|
||||||
|
'name' => $data['name'],
|
||||||
|
'mount_path' => $data['mount_path'],
|
||||||
|
'host_path' => $data['host_path'],
|
||||||
|
'resource_id' => $this->resource->id,
|
||||||
|
'resource_type' => $this->resource->getMorphClass(),
|
||||||
|
]);
|
||||||
|
$this->resource->refresh();
|
||||||
|
$this->emit('success', 'Storage added successfully');
|
||||||
|
$this->emit('clearAddStorage');
|
||||||
|
$this->emit('refreshStorages');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
40
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
40
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class GetLogs extends Component
|
||||||
|
{
|
||||||
|
public string $outputs = '';
|
||||||
|
public string $errors = '';
|
||||||
|
public Server $server;
|
||||||
|
public ?string $container = null;
|
||||||
|
public ?bool $streamLogs = false;
|
||||||
|
public int $numberOfLines = 100;
|
||||||
|
public function doSomethingWithThisChunkOfOutput($output)
|
||||||
|
{
|
||||||
|
$this->outputs .= $output;
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function getLogs($refresh = false)
|
||||||
|
{
|
||||||
|
if ($this->container) {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
||||||
|
if ($refresh) {
|
||||||
|
$this->outputs = '';
|
||||||
|
}
|
||||||
|
Process::run($sshCommand, function (string $type, string $output) {
|
||||||
|
$this->doSomethingWithThisChunkOfOutput($output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.get-logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ class HealthChecks extends Component
|
|||||||
|
|
||||||
public $resource;
|
public $resource;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
'resource.health_check_enabled' => 'boolean',
|
||||||
'resource.health_check_path' => 'string',
|
'resource.health_check_path' => 'string',
|
||||||
'resource.health_check_port' => 'nullable|string',
|
'resource.health_check_port' => 'nullable|string',
|
||||||
'resource.health_check_host' => 'string',
|
'resource.health_check_host' => 'string',
|
||||||
@@ -22,12 +23,19 @@ class HealthChecks extends Component
|
|||||||
'resource.health_check_start_period' => 'integer',
|
'resource.health_check_start_period' => 'integer',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->resource->save();
|
||||||
|
$this->emit('success', 'Health check updated.');
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->resource->save();
|
$this->resource->save();
|
||||||
$this->emit('saved');
|
$this->emit('success', 'Health check updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
53
app/Http/Livewire/Project/Shared/Logs.php
Normal file
53
app/Http/Livewire/Project/Shared/Logs.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Logs extends Component
|
||||||
|
{
|
||||||
|
public ?string $type = null;
|
||||||
|
public Application|StandalonePostgresql|Service $resource;
|
||||||
|
public Server $server;
|
||||||
|
public ?string $container = null;
|
||||||
|
public $parameters;
|
||||||
|
public $query;
|
||||||
|
public $status;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
$this->type = 'application';
|
||||||
|
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
$this->container = data_get($containers[0], 'Names');
|
||||||
|
}
|
||||||
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
|
$this->type = 'database';
|
||||||
|
$this->resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$this->container = $this->resource->uuid;
|
||||||
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
|
$this->type = 'service';
|
||||||
|
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->server;
|
||||||
|
$this->container = data_get($this->parameters, 'service_name') . '-' . $this->resource->uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Add extends Component
|
class Add extends Component
|
||||||
{
|
{
|
||||||
|
public $uuid;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $mount_path;
|
public string $mount_path;
|
||||||
@@ -31,8 +32,9 @@ class Add extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->emitUp('submit', [
|
$name = $this->uuid . '-' . $this->name;
|
||||||
'name' => $this->name,
|
$this->emit('addNewVolume', [
|
||||||
|
'name' => $name,
|
||||||
'mount_path' => $this->mount_path,
|
'mount_path' => $this->mount_path,
|
||||||
'host_path' => $this->host_path,
|
'host_path' => $this->host_path,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -8,28 +8,10 @@ use Livewire\Component;
|
|||||||
class All extends Component
|
class All extends Component
|
||||||
{
|
{
|
||||||
public $resource;
|
public $resource;
|
||||||
protected $listeners = ['refreshStorages', 'submit'];
|
protected $listeners = ['refreshStorages'];
|
||||||
|
|
||||||
public function refreshStorages()
|
public function refreshStorages()
|
||||||
{
|
{
|
||||||
$this->resource->refresh();
|
$this->resource->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit($data)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
LocalPersistentVolume::create([
|
|
||||||
'name' => $data['name'],
|
|
||||||
'mount_path' => $data['mount_path'],
|
|
||||||
'host_path' => $data['host_path'],
|
|
||||||
'resource_id' => $this->resource->id,
|
|
||||||
'resource_type' => $this->resource->getMorphClass(),
|
|
||||||
]);
|
|
||||||
$this->resource->refresh();
|
|
||||||
$this->emit('success', 'Storage added successfully');
|
|
||||||
$this->emit('clearAddStorage');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 bool $isFirst = true;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'storage.name' => 'required|string',
|
'storage.name' => 'required|string',
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
if (config('coolify.waitlist') == false) {
|
||||||
|
return redirect()->route('register');
|
||||||
|
}
|
||||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||||
$this->users = User::count();
|
$this->users = User::count();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->build_workdir = "{$this->workdir}" . rtrim($this->application->base_directory, '/');
|
$this->build_workdir = "{$this->workdir}" . rtrim($this->application->base_directory, '/');
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
|
||||||
$this->container_name = generateApplicationContainerName($this->application);
|
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||||
savePrivateKeyToFs($this->server);
|
savePrivateKeyToFs($this->server);
|
||||||
$this->saved_outputs = collect();
|
$this->saved_outputs = collect();
|
||||||
|
|
||||||
@@ -97,7 +97,9 @@ 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 = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
|
if (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();
|
||||||
@@ -231,7 +233,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
);
|
);
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
||||||
ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->generate_build_env_variables();
|
$this->generate_build_env_variables();
|
||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
@@ -256,7 +258,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
||||||
ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
|
|
||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
@@ -284,7 +286,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function rolling_update()
|
private function rolling_update()
|
||||||
{
|
{
|
||||||
if (count($this->application->ports_mappings_array) > 0){
|
if (count($this->application->ports_mappings_array) > 0) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
||||||
);
|
);
|
||||||
@@ -301,7 +303,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
private function health_check()
|
private function health_check()
|
||||||
{
|
{
|
||||||
ray('New container name: ', $this->container_name);
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
|
$this->newVersionIsHealthy = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ray('New container name: ', $this->container_name);
|
||||||
if ($this->container_name) {
|
if ($this->container_name) {
|
||||||
$counter = 0;
|
$counter = 0;
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
@@ -348,7 +354,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
||||||
ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
||||||
]);
|
]);
|
||||||
@@ -373,9 +379,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
{
|
{
|
||||||
$pull = "--pull=always";
|
$pull = "--pull=always";
|
||||||
if (isDev()) {
|
|
||||||
$pull = "--pull=never";
|
|
||||||
}
|
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||||
|
|
||||||
@@ -399,7 +402,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
"echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$this->workdir}. '"
|
"echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} (commit sha {$this->application->git_commit_sha}) to {$this->workdir}. '"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$this->importing_git_repository()
|
$this->importing_git_repository()
|
||||||
@@ -571,6 +574,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
|
data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck');
|
||||||
|
}
|
||||||
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
|
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
|
||||||
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
|
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -622,14 +628,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function generate_environment_variables($ports)
|
private function generate_environment_variables($ports)
|
||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
ray('Generate Environment Variables')->green();
|
// ray('Generate Environment Variables')->green();
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
ray($this->application->runtime_environment_variables)->green();
|
// ray($this->application->runtime_environment_variables)->green();
|
||||||
foreach ($this->application->runtime_environment_variables as $env) {
|
foreach ($this->application->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->value");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ray($this->application->runtime_environment_variables_preview)->green();
|
// ray($this->application->runtime_environment_variables_preview)->green();
|
||||||
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->value");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 +101,16 @@ 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));
|
||||||
$labelId = data_get($labels, 'coolify.applicationId');
|
$labelId = data_get($labels, 'coolify.applicationId');
|
||||||
if ($labelId) {
|
if ($labelId) {
|
||||||
if (str_contains($labelId, '-pr-')) {
|
if (str_contains($labelId, '-pr-')) {
|
||||||
$previewId = (int) Str::after($labelId, '-pr-');
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
$applicationId = (int) Str::before($labelId, '-pr-');
|
$applicationId = (int) Str::before($labelId, '-pr-');
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $previewId)->first();
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
if ($preview) {
|
if ($preview) {
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
$statusFromDb = $preview->status;
|
$statusFromDb = $preview->status;
|
||||||
@@ -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') && $this->database->id !== 0) {
|
||||||
ray('database not running');
|
ray('database not running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -89,7 +90,6 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->upload_to_s3();
|
$this->upload_to_s3();
|
||||||
}
|
}
|
||||||
$this->save_backup_logs();
|
$this->save_backup_logs();
|
||||||
// TODO: Notify user
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,16 @@ class Application extends BaseModel
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleting(function ($application) {
|
static::deleting(function ($application) {
|
||||||
|
// Stop Container
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$application->uuid}"],
|
||||||
|
$application->destination->server,
|
||||||
|
false
|
||||||
|
);
|
||||||
$application->settings()->delete();
|
$application->settings()->delete();
|
||||||
$storages = $application->persistentStorages()->get();
|
$storages = $application->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $application->destination->server);
|
instant_remote_process(["docker volume rm -f $storage->name"], $application->destination->server, false);
|
||||||
}
|
}
|
||||||
$application->persistentStorages()->delete();
|
$application->persistentStorages()->delete();
|
||||||
$application->environment_variables()->delete();
|
$application->environment_variables()->delete();
|
||||||
@@ -38,6 +44,10 @@ class Application 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 type()
|
public function type()
|
||||||
{
|
{
|
||||||
@@ -212,13 +222,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,9 +236,17 @@ 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;
|
||||||
}
|
}
|
||||||
|
public function isHealthcheckDisabled(): bool
|
||||||
|
{
|
||||||
|
if (data_get($this, 'dockerfile') || data_get($this, 'build_pack') === 'dockerfile' || data_get($this, 'health_check_enabled') === false) {
|
||||||
|
ray('dockerfile');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,37 @@ 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);
|
||||||
|
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 (!$fileVolume->is_directory && $isDir == 'NOK') {
|
||||||
|
$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");
|
||||||
|
}
|
||||||
|
ray($commands->toArray());
|
||||||
|
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
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Project extends BaseModel
|
|||||||
'project_id' => $project->id,
|
'project_id' => $project->id,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleted(function ($project) {
|
static::deleting(function ($project) {
|
||||||
$project->environments()->delete();
|
$project->environments()->delete();
|
||||||
$project->settings()->delete();
|
$project->settings()->delete();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use App\Enums\ProxyStatus;
|
use App\Enums\ProxyStatus;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||||
|
|
||||||
@@ -78,7 +79,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 +117,26 @@ 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 getIp(): Attribute
|
||||||
public function previews() {
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: function () {
|
||||||
|
if (isDev()) {
|
||||||
|
return '127.0.0.1';
|
||||||
|
}
|
||||||
|
if ($this->ip === 'host.docker.internal') {
|
||||||
|
return base_ip();
|
||||||
|
}
|
||||||
|
return $this->ip;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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 +178,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 +195,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,13 +2,12 @@
|
|||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
class Service extends BaseModel
|
class Service extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -17,9 +16,10 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
static::deleted(function ($service) {
|
static::deleting(function ($service) {
|
||||||
$storagesToDelete = collect([]);
|
$storagesToDelete = collect([]);
|
||||||
foreach ($service->applications()->get() as $application) {
|
foreach ($service->applications()->get() as $application) {
|
||||||
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server, false);
|
||||||
$storages = $application->persistentStorages()->get();
|
$storages = $application->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
@@ -27,6 +27,7 @@ class Service extends BaseModel
|
|||||||
$application->persistentStorages()->delete();
|
$application->persistentStorages()->delete();
|
||||||
}
|
}
|
||||||
foreach ($service->databases()->get() as $database) {
|
foreach ($service->databases()->get() as $database) {
|
||||||
|
instant_remote_process(["docker rm -f {$database->name}-{$service->uuid}"], $service->server, false);
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
@@ -41,6 +42,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 +50,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);
|
||||||
@@ -56,6 +64,10 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ServiceDatabase::class);
|
return $this->hasMany(ServiceDatabase::class);
|
||||||
}
|
}
|
||||||
|
public function destination()
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
public function environment()
|
public function environment()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Environment::class);
|
return $this->belongsTo(Environment::class);
|
||||||
@@ -80,9 +92,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 +125,108 @@ 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;
|
|
||||||
|
|
||||||
$volumes = collect([]);
|
$generatedServiceFQDNS = collect([]);
|
||||||
$envs = collect([]);
|
if (is_null($this->destination)) {
|
||||||
$ports = collect([]);
|
$destination = $this->server->destinations()->first();
|
||||||
|
if ($destination) {
|
||||||
|
$this->destination()->associate($destination);
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$definedNetwork = collect([$this->uuid]);
|
||||||
|
|
||||||
$services = collect($services)->map(function ($service, $serviceName) use ($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 +236,342 @@ 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', []));
|
// Add Coolify specific networks
|
||||||
|
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||||
|
return $value == $definedNetwork;
|
||||||
|
});
|
||||||
|
if (!$definedNetworkExists) {
|
||||||
|
foreach ($definedNetwork as $network) {
|
||||||
|
$topLevelNetworks->put($network, [
|
||||||
|
'name' => $network,
|
||||||
|
'external' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$networks = $serviceNetworks->toArray();
|
||||||
|
foreach ($definedNetwork as $key => $network) {
|
||||||
|
$networks = array_merge($networks, [
|
||||||
|
$network
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
data_set($service, 'networks', $networks);
|
||||||
|
|
||||||
|
// Collect/create/update volumes
|
||||||
if ($serviceVolumes->count() > 0) {
|
if ($serviceVolumes->count() > 0) {
|
||||||
LocalPersistentVolume::whereResourceId($savedService->id)->whereResourceType(get_class($savedService))->delete();
|
$serviceVolumes = $serviceVolumes->map(function ($volume) use ($savedService, $topLevelVolumes) {
|
||||||
foreach ($serviceVolumes as $volume) {
|
$type = null;
|
||||||
if (is_string($volume) && Str::startsWith($volume, './')) {
|
$source = null;
|
||||||
// Local file
|
$target = null;
|
||||||
$fsPath = Str::before($volume, ':');
|
$content = null;
|
||||||
$volumePath = Str::of($volume)->after(':')->beforeLast(':');
|
$isDirectory = false;
|
||||||
ray($fsPath, $volumePath);
|
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) {
|
||||||
|
$contentNotNull = data_get($foundConfig, 'content');
|
||||||
|
if ($contentNotNull) {
|
||||||
|
$content = $contentNotNull;
|
||||||
|
}
|
||||||
|
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type->value() === 'bind') {
|
||||||
|
if ($source->value() === "/var/run/docker.sock") {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
|
return $volume;
|
||||||
|
}
|
||||||
LocalFileVolume::updateOrCreate(
|
LocalFileVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
'mount_path' => $volumePath,
|
'mount_path' => $target,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $savedService->id,
|
||||||
'resource_type' => get_class($savedService)
|
'resource_type' => get_class($savedService)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'fs_path' => $fsPath,
|
'fs_path' => $source,
|
||||||
'mount_path' => $volumePath,
|
'mount_path' => $target,
|
||||||
|
'content' => $content,
|
||||||
|
'is_directory' => $isDirectory,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $savedService->id,
|
||||||
'resource_type' => get_class($savedService)
|
'resource_type' => get_class($savedService)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
continue;
|
} else if ($type->value() === 'volume') {
|
||||||
}
|
$slugWithoutUuid = Str::slug($source, '-');
|
||||||
if (is_string($volume)) {
|
$name = "{$savedService->service->uuid}_{$slugWithoutUuid}";
|
||||||
$volumeName = Str::before($volume, ':');
|
if (is_string($volume)) {
|
||||||
$volumePath = Str::after($volume, ':');
|
$source = Str::of($volume)->before(':');
|
||||||
}
|
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||||
if (is_array($volume)) {
|
$source = $name;
|
||||||
$volumeName = data_get($volume, 'source');
|
$volume = "$source:$target";
|
||||||
$volumePath = data_get($volume, 'target');
|
} else if (is_array($volume)) {
|
||||||
}
|
data_set($volume, 'source', $name);
|
||||||
|
|
||||||
$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)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
$topLevelVolumes->put($name, [
|
||||||
|
'name' => $name,
|
||||||
|
]);
|
||||||
|
LocalPersistentVolume::updateOrCreate(
|
||||||
|
[
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => $name,
|
||||||
|
'mount_path' => $target,
|
||||||
|
'resource_id' => $savedService->id,
|
||||||
|
'resource_type' => get_class($savedService)
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
$savedService->getFilesFromServer(isInit: true);
|
||||||
|
return $volume;
|
||||||
|
});
|
||||||
|
data_set($service, 'volumes', $serviceVolumes->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect and add networks
|
// Add env_file with at least .env to the service
|
||||||
$serviceNetworks = collect(data_get($service, 'networks', []));
|
// $envFile = collect(data_get($service, 'env_file', []));
|
||||||
if ($serviceNetworks->count() > 0) {
|
// if ($envFile->count() > 0) {
|
||||||
foreach ($serviceNetworks as $networkName => $networkDetails) {
|
// if (!$envFile->contains('.env')) {
|
||||||
$networkExists = $composeNetworks->contains(function ($value, $key) use ($networkName) {
|
// $envFile->push('.env');
|
||||||
return $value == $networkName || $key == $networkName;
|
// }
|
||||||
});
|
// } else {
|
||||||
if (!$networkExists) {
|
// $envFile = collect(['.env']);
|
||||||
$composeNetworks->put($networkDetails, null);
|
// }
|
||||||
}
|
// data_set($service, 'env_file', $envFile->toArray());
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add Coolify specific networks
|
|
||||||
$definedNetworkExists = $composeNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
|
||||||
return $value == $definedNetwork;
|
|
||||||
});
|
|
||||||
if (!$definedNetworkExists) {
|
|
||||||
$composeNetworks->put($definedNetwork, [
|
|
||||||
'name' => $definedNetwork,
|
|
||||||
'external' => false
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$networks = $serviceNetworks->toArray();
|
|
||||||
$networks = array_merge($networks, [$definedNetwork]);
|
|
||||||
data_set($service, 'networks', $networks);
|
|
||||||
|
|
||||||
|
|
||||||
// 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 ($isNew) {
|
||||||
|
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
||||||
|
$fqdn = generateFqdn($this->server, "{$name->value()}-{$this->uuid}");
|
||||||
|
if (substr_count($key->value(), '_') === 3) {
|
||||||
|
// SERVICE_FQDN_UMAMI_1000
|
||||||
|
$port = $key->afterLast('_');
|
||||||
|
} else {
|
||||||
|
// SERVICE_FQDN_UMAMI
|
||||||
|
$port = null;
|
||||||
|
}
|
||||||
|
if ($port) {
|
||||||
|
$fqdn = "$fqdn:$port";
|
||||||
|
}
|
||||||
|
if (substr_count($key->value(), '_') >= 2) {
|
||||||
|
if (is_null($value)) {
|
||||||
|
$value = Str::of('/');
|
||||||
}
|
}
|
||||||
if ($nakedValue->startsWith('+')) {
|
$path = $value->value();
|
||||||
$nakedValue = Str::of($nakedValue)->after('+');
|
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 (!$envs->has($nakedName->value())) {
|
$fqdn = "$fqdn$path";
|
||||||
$envs->put($nakedName->value(), $nakedValue->value());
|
}
|
||||||
EnvironmentVariable::updateOrCreate([
|
|
||||||
'key' => $nakedName->value(),
|
if (!$isDatabase) {
|
||||||
'service_id' => $this->id,
|
if ($savedService->fqdn) {
|
||||||
], [
|
$fqdn = $savedService->fqdn . ',' . $fqdn;
|
||||||
'value' => $nakedValue->value(),
|
} else {
|
||||||
|
$fqdn = $fqdn;
|
||||||
|
}
|
||||||
|
$savedService->fqdn = $fqdn;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// data_forget($service, "environment.$variableName");
|
||||||
|
// $yaml = data_forget($yaml, "services.$serviceName.environment.$variableName");
|
||||||
|
// if (count(data_get($yaml, 'services.' . $serviceName . '.environment')) === 0) {
|
||||||
|
// $yaml = data_forget($yaml, "services.$serviceName.environment");
|
||||||
|
// }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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') {
|
||||||
|
if (Str::lower($forService) === $serviceName) {
|
||||||
|
$fqdn = generateFqdn($this->server, $containerName);
|
||||||
|
} else {
|
||||||
|
$fqdn = generateFqdn($this->server, Str::lower($forService) . '-' . $this->uuid);
|
||||||
|
}
|
||||||
|
if ($foundEnv) {
|
||||||
|
$fqdn = data_get($foundEnv, 'value');
|
||||||
|
} else {
|
||||||
|
if ($command->value() === 'URL') {
|
||||||
|
$fqdn = Str::of($fqdn)->after('://')->value();
|
||||||
|
}
|
||||||
|
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,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
if (!$isDatabase) {
|
||||||
if (!$envs->has($nakedName->value())) {
|
if ($command->value() === 'FQDN') {
|
||||||
$envs->put($nakedName->value(), null);
|
$savedService->fqdn = $fqdn;
|
||||||
$envExists = EnvironmentVariable::where('service_id', $this->id)->where('key', $nakedName->value())->exists();
|
$savedService->save();
|
||||||
if (!$envExists) {
|
|
||||||
EnvironmentVariable::create([
|
|
||||||
'key' => $nakedName->value(),
|
|
||||||
'value' => null,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$variableName = Str::of(replaceVariables(Str::of($value)));
|
|
||||||
$generatedValue = null;
|
|
||||||
if ($variableName->startsWith('SERVICE_USER')) {
|
|
||||||
$variableDefined = EnvironmentVariable::whereServiceId($this->id)->where('key', $variableName->value())->first();
|
|
||||||
if (!$variableDefined) {
|
|
||||||
$generatedValue = Str::random(10);
|
|
||||||
} else {
|
} else {
|
||||||
$generatedValue = $variableDefined->value;
|
switch ($command) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$foundEnv) {
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $generatedValue,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!$envs->has($variableName->value())) {
|
} else {
|
||||||
$envs->put($variableName->value(), $generatedValue);
|
if ($value->contains(':-')) {
|
||||||
EnvironmentVariable::updateOrCreate([
|
$key = $value->before(':');
|
||||||
'key' => $variableName->value(),
|
$defaultValue = $value->after(':-');
|
||||||
'service_id' => $this->id,
|
} else if ($value->contains('-')) {
|
||||||
], [
|
$key = $value->before('-');
|
||||||
'value' => $generatedValue,
|
$defaultValue = $value->after('-');
|
||||||
'is_build_time' => false,
|
} else if ($value->contains(':?')) {
|
||||||
'service_id' => $this->id,
|
$key = $value->before(':');
|
||||||
'is_preview' => false,
|
$defaultValue = $value->after(':?');
|
||||||
]);
|
} else if ($value->contains('?')) {
|
||||||
}
|
$key = $value->before('?');
|
||||||
} else if ($variableName->startsWith('SERVICE_PASSWORD')) {
|
$defaultValue = $value->after('?');
|
||||||
$variableDefined = EnvironmentVariable::whereServiceId($this->id)->where('key', $variableName->value())->first();
|
|
||||||
if (!$variableDefined) {
|
|
||||||
$generatedValue = Str::password(symbols: false);
|
|
||||||
} 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_FQDN')) {
|
|
||||||
if ($fqdns) {
|
|
||||||
$number = Str::of($variableName)->after('SERVICE_FQDN')->afterLast('_')->value();
|
|
||||||
if (is_numeric($number)) {
|
|
||||||
$number = (int) $number - 1;
|
|
||||||
} else {
|
|
||||||
$number = 0;
|
|
||||||
}
|
|
||||||
$fqdn = getFqdnWithoutPort(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 = getFqdnWithoutPort(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, 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_') && !Str::of($value)->startsWith('SERVICE_')) {
|
||||||
|
$k = Str::of($value)->before("=");
|
||||||
|
$v = Str::of($value)->after("=");
|
||||||
|
$withoutServiceEnvs->put($k->value(), $v->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(bool $isInit = false)
|
||||||
|
{
|
||||||
|
getFilesystemVolumesFromServer($this, $isInit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class ServiceDatabase extends BaseModel
|
class ServiceDatabase extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -14,10 +13,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 +21,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(bool $isInit = false)
|
||||||
|
{
|
||||||
|
getFilesystemVolumesFromServer($this, $isInit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,21 @@ class StandalonePostgresql extends BaseModel
|
|||||||
'is_readonly' => true
|
'is_readonly' => true
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleted(function ($database) {
|
static::deleting(function ($database) {
|
||||||
|
// Stop Container
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$database->uuid}"],
|
||||||
|
$database->destination->server,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// Stop TCP Proxy
|
||||||
|
if ($database->is_public) {
|
||||||
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server, false);
|
||||||
|
}
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
$database->environment_variables()->delete();
|
$database->environment_variables()->delete();
|
||||||
|
// Remove Volume
|
||||||
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -65,6 +76,11 @@ class StandalonePostgresql extends BaseModel
|
|||||||
return $this->belongsTo(Environment::class);
|
return $this->belongsTo(Environment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
public function destination()
|
public function destination()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo();
|
||||||
|
|||||||
@@ -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());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ const DATABASE_DOCKER_IMAGES = [
|
|||||||
'couchdb',
|
'couchdb',
|
||||||
'neo4j',
|
'neo4j',
|
||||||
'influxdb',
|
'influxdb',
|
||||||
'clickhouse'
|
'clickhouse/clickhouse-server'
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
|
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
|
||||||
{
|
{
|
||||||
@@ -104,16 +104,16 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
|||||||
return data_get($container[0], 'State.Status', 'exited');
|
return data_get($container[0], 'State.Status', 'exited');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateApplicationContainerName(Application $application)
|
function generateApplicationContainerName(Application $application, $pull_request_id = 0)
|
||||||
{
|
{
|
||||||
$now = now()->format('Hisu');
|
$now = now()->format('Hisu');
|
||||||
if ($application->pull_request_id !== 0 && $application->pull_request_id !== null) {
|
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||||
return $application->uuid . '-pr-' . $application->pull_request_id;
|
return $application->uuid . '-pr-' . $pull_request_id;
|
||||||
} else {
|
} else {
|
||||||
return $application->uuid . '-' . $now;
|
return $application->uuid . '-' . $now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function get_port_from_dockerfile($dockerfile): int
|
function get_port_from_dockerfile($dockerfile): int|null
|
||||||
{
|
{
|
||||||
$dockerfile_array = explode("\n", $dockerfile);
|
$dockerfile_array = explode("\n", $dockerfile);
|
||||||
$found_exposed_port = null;
|
$found_exposed_port = null;
|
||||||
@@ -127,10 +127,10 @@ function get_port_from_dockerfile($dockerfile): int
|
|||||||
if ($found_exposed_port) {
|
if ($found_exposed_port) {
|
||||||
return (int)$found_exposed_port->value();
|
return (int)$found_exposed_port->value();
|
||||||
}
|
}
|
||||||
return 80;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,22 +141,26 @@ 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, bool $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) {
|
||||||
|
$uuid = (string)new Cuid2(7);
|
||||||
$url = Url::fromString($domain);
|
$url = Url::fromString($domain);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
$schema = $url->getScheme();
|
$schema = $url->getScheme();
|
||||||
$port = $url->getPort();
|
$port = $url->getPort();
|
||||||
$slug = Str::slug($host . $path);
|
|
||||||
|
|
||||||
$http_label = "{$container_name}-{$slug}-http";
|
$http_label = "{$uuid}-http";
|
||||||
$https_label = "{$container_name}-{$slug}-https";
|
$https_label = "{$uuid}-https";
|
||||||
|
|
||||||
if ($schema === 'https') {
|
if ($schema === 'https') {
|
||||||
// Set labels for https
|
// Set labels for https
|
||||||
@@ -203,10 +207,10 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
{
|
{
|
||||||
|
|
||||||
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
||||||
$container_name = generateApplicationContainerName($application);
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
$appId = $application->id;
|
$appId = $application->id;
|
||||||
if ($pull_request_id !== 0) {
|
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||||
$appId = $appId . '-pr-' . $application->pull_request_id;
|
$appId = $appId . '-pr-' . $pull_request_id;
|
||||||
}
|
}
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
|
$labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
|
||||||
@@ -217,7 +221,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
}
|
}
|
||||||
// Add Traefik labels no matter which proxy is selected
|
// 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, $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)
|
||||||
|
|||||||
@@ -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,135 @@ 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, bool $isInit = false)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
ray($path,$fileLocation);
|
||||||
|
// Exists and is a file
|
||||||
|
$isFile = instant_remote_process(["test -f $fileLocation && echo OK || echo NOK"], $server);
|
||||||
|
// Exists and is a directory
|
||||||
|
$isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server);
|
||||||
|
|
||||||
|
if ($isFile == 'OK') {
|
||||||
|
// If its a file & exists
|
||||||
|
$filesystemContent = instant_remote_process(["cat $fileLocation"], $server);
|
||||||
|
$fileVolume->content = $filesystemContent;
|
||||||
|
$fileVolume->is_directory = false;
|
||||||
|
$fileVolume->save();
|
||||||
|
} else if ($isDir == 'OK') {
|
||||||
|
// If its a directory & exists
|
||||||
|
$fileVolume->content = null;
|
||||||
|
$fileVolume->is_directory = true;
|
||||||
|
$fileVolume->save();
|
||||||
|
} else if ($isFile == 'NOK' && $isDir == 'NOK' && !$fileVolume->is_directory && $isInit && $content) {
|
||||||
|
// Does not exists (no dir or file), not flagged as directory, is init, has content
|
||||||
|
$fileVolume->content = $content;
|
||||||
|
$fileVolume->is_directory = false;
|
||||||
|
$fileVolume->save();
|
||||||
|
$content = base64_encode($content);
|
||||||
|
$dir = Str::of($fileLocation)->dirname();
|
||||||
|
instant_remote_process([
|
||||||
|
"mkdir -p $dir",
|
||||||
|
"echo '$content' | base64 -d > $fileLocation"
|
||||||
|
], $server);
|
||||||
|
} else if ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) {
|
||||||
|
$fileVolume->content = null;
|
||||||
|
$fileVolume->is_directory = true;
|
||||||
|
$fileVolume->save();
|
||||||
|
instant_remote_process(["mkdir -p $fileLocation"], $server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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();
|
||||||
|
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||||
|
if ($generatedEnv) {
|
||||||
|
$generatedEnv->value = $resource->fqdn;
|
||||||
|
$generatedEnv->save();
|
||||||
|
}
|
||||||
|
$variableName = "SERVICE_URL_" . Str::of($resource->name)->upper();
|
||||||
|
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||||
|
if ($generatedEnv) {
|
||||||
|
$url = Str::of($resource->fqdn)->after('://');
|
||||||
|
$generatedEnv->value = $url;
|
||||||
|
$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,14 @@ 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\File;
|
||||||
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;
|
||||||
@@ -100,8 +104,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
|
|||||||
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
|
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
|
||||||
}
|
}
|
||||||
if (isset($livewire)) {
|
if (isset($livewire)) {
|
||||||
$livewire->emit('error', $message);
|
return $livewire->emit('error', $message);
|
||||||
throw new RuntimeException($message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException($message);
|
throw new RuntimeException($message);
|
||||||
@@ -386,3 +389,59 @@ 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();
|
||||||
|
$deprecated = File::get(base_path('templates/deprecated.json'));
|
||||||
|
$deprecated = collect(json_decode($deprecated))->sortKeys();
|
||||||
|
$services = $services->merge($deprecated);
|
||||||
|
$version = config('version');
|
||||||
|
$services = $services->map(function ($service) use ($version) {
|
||||||
|
if (version_compare($version, data_get($service,'minVersion', '0.0.0'), '<')) {
|
||||||
|
$service->disabled = true;
|
||||||
|
}
|
||||||
|
return $service;
|
||||||
|
});
|
||||||
|
} 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.45',
|
'release' => '4.0.0-beta.66',
|
||||||
// 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.45';
|
return '4.0.0-beta.66';
|
||||||
|
|||||||
@@ -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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->boolean('health_check_enabled')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('health_check_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->nullableMorphs('destination');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->dropMorphs('destination');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ services:
|
|||||||
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
||||||
POSTGRES_HOST_AUTH_METHOD: "trust"
|
POSTGRES_HOST_AUTH_METHOD: "trust"
|
||||||
volumes:
|
volumes:
|
||||||
- /data/coolify/_volumes/database/:/var/lib/postgresql/data
|
- coolify-pg-data-dev:/var/lib/postgresql/data
|
||||||
redis:
|
redis:
|
||||||
ports:
|
ports:
|
||||||
- "${FORWARD_REDIS_PORT:-6379}:6379"
|
- "${FORWARD_REDIS_PORT:-6379}:6379"
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
- /data/coolify/_volumes/redis/:/data
|
- coolify-redis-data-dev:/data
|
||||||
vite:
|
vite:
|
||||||
image: node:19
|
image: node:19
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /:/host
|
- /:/host
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- /data/coolify/:/data/coolify
|
- coolify-data-dev:/data/coolify
|
||||||
mailpit:
|
mailpit:
|
||||||
image: "axllent/mailpit:latest"
|
image: "axllent/mailpit:latest"
|
||||||
container_name: coolify-mail
|
container_name: coolify-mail
|
||||||
@@ -76,6 +76,12 @@ services:
|
|||||||
MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}"
|
MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}"
|
||||||
MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}"
|
MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}"
|
||||||
volumes:
|
volumes:
|
||||||
- /data/coolify/_volumes/minio/:/data
|
- coolify-minio-data-dev:/data
|
||||||
networks:
|
networks:
|
||||||
- coolify
|
- coolify
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
coolify-data-dev:
|
||||||
|
coolify-pg-data-dev:
|
||||||
|
coolify-redis-data-dev:
|
||||||
|
coolify-minio-data-dev:
|
||||||
|
|||||||
756
examples/compose/appwrite.yaml
Normal file
756
examples/compose/appwrite.yaml
Normal file
@@ -0,0 +1,756 @@
|
|||||||
|
x-logging: &x-logging
|
||||||
|
logging:
|
||||||
|
driver: 'json-file'
|
||||||
|
options:
|
||||||
|
max-file: '5'
|
||||||
|
max-size: '10m'
|
||||||
|
|
||||||
|
x-image: &x-image
|
||||||
|
image: appwrite/appwrite:1.4.3
|
||||||
|
|
||||||
|
x-image-assistant: &x-image-assistant
|
||||||
|
image: appwrite/assistant:0.2.1
|
||||||
|
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
appwrite:
|
||||||
|
<<: *x-image
|
||||||
|
container_name: appwrite
|
||||||
|
<<: *x-logging
|
||||||
|
labels:
|
||||||
|
- traefik.constraint-label-stack=appwrite
|
||||||
|
- traefik.docker.network=appwrite
|
||||||
|
- traefik.http.services.appwrite_api.loadbalancer.server.port=80
|
||||||
|
#http
|
||||||
|
- traefik.http.routers.appwrite_api_http.entrypoints=web
|
||||||
|
- traefik.http.routers.appwrite_api_http.rule=PathPrefix(`/`)
|
||||||
|
- traefik.http.routers.appwrite_api_http.service=appwrite_api
|
||||||
|
# https
|
||||||
|
- traefik.http.routers.appwrite_api_https.entrypoints=websecure
|
||||||
|
- traefik.http.routers.appwrite_api_https.rule=PathPrefix(`/`)
|
||||||
|
- traefik.http.routers.appwrite_api_https.service=appwrite_api
|
||||||
|
- traefik.http.routers.appwrite_api_https.tls=true
|
||||||
|
volumes:
|
||||||
|
- appwrite-uploads:/storage/uploads:rw
|
||||||
|
- appwrite-cache:/storage/cache:rw
|
||||||
|
- appwrite-config:/storage/config:rw
|
||||||
|
- appwrite-certificates:/storage/certificates:rw
|
||||||
|
- appwrite-functions:/storage/functions:rw
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
- redis
|
||||||
|
# - clamav
|
||||||
|
- influxdb
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_APPWRITE=/
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_LOCALE
|
||||||
|
- _APP_CONSOLE_WHITELIST_ROOT
|
||||||
|
- _APP_CONSOLE_WHITELIST_EMAILS
|
||||||
|
- _APP_CONSOLE_WHITELIST_IPS
|
||||||
|
- _APP_SYSTEM_EMAIL_NAME
|
||||||
|
- _APP_SYSTEM_EMAIL_ADDRESS
|
||||||
|
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
|
||||||
|
- _APP_SYSTEM_RESPONSE_FORMAT
|
||||||
|
- _APP_OPTIONS_ABUSE
|
||||||
|
- _APP_OPTIONS_FORCE_HTTPS
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_DOMAIN
|
||||||
|
- _APP_DOMAIN_TARGET
|
||||||
|
- _APP_DOMAIN_FUNCTIONS
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_SMTP_HOST
|
||||||
|
- _APP_SMTP_PORT
|
||||||
|
- _APP_SMTP_SECURE
|
||||||
|
- _APP_SMTP_USERNAME
|
||||||
|
- _APP_SMTP_PASSWORD
|
||||||
|
- _APP_USAGE_STATS
|
||||||
|
- _APP_INFLUXDB_HOST
|
||||||
|
- _APP_INFLUXDB_PORT
|
||||||
|
- _APP_STORAGE_LIMIT
|
||||||
|
- _APP_STORAGE_PREVIEW_LIMIT
|
||||||
|
- _APP_STORAGE_ANTIVIRUS
|
||||||
|
- _APP_STORAGE_ANTIVIRUS_HOST
|
||||||
|
- _APP_STORAGE_ANTIVIRUS_PORT
|
||||||
|
- _APP_STORAGE_DEVICE
|
||||||
|
- _APP_STORAGE_S3_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_S3_SECRET
|
||||||
|
- _APP_STORAGE_S3_REGION
|
||||||
|
- _APP_STORAGE_S3_BUCKET
|
||||||
|
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_DO_SPACES_SECRET
|
||||||
|
- _APP_STORAGE_DO_SPACES_REGION
|
||||||
|
- _APP_STORAGE_DO_SPACES_BUCKET
|
||||||
|
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_BACKBLAZE_SECRET
|
||||||
|
- _APP_STORAGE_BACKBLAZE_REGION
|
||||||
|
- _APP_STORAGE_BACKBLAZE_BUCKET
|
||||||
|
- _APP_STORAGE_LINODE_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_LINODE_SECRET
|
||||||
|
- _APP_STORAGE_LINODE_REGION
|
||||||
|
- _APP_STORAGE_LINODE_BUCKET
|
||||||
|
- _APP_STORAGE_WASABI_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_WASABI_SECRET
|
||||||
|
- _APP_STORAGE_WASABI_REGION
|
||||||
|
- _APP_STORAGE_WASABI_BUCKET
|
||||||
|
- _APP_FUNCTIONS_SIZE_LIMIT
|
||||||
|
- _APP_FUNCTIONS_TIMEOUT
|
||||||
|
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||||
|
- _APP_FUNCTIONS_CPUS
|
||||||
|
- _APP_FUNCTIONS_MEMORY
|
||||||
|
- _APP_FUNCTIONS_RUNTIMES
|
||||||
|
- _APP_EXECUTOR_SECRET
|
||||||
|
- _APP_EXECUTOR_HOST
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_STATSD_HOST
|
||||||
|
- _APP_STATSD_PORT
|
||||||
|
- _APP_MAINTENANCE_INTERVAL
|
||||||
|
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||||
|
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||||
|
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||||
|
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||||
|
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||||
|
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||||
|
- _APP_SMS_PROVIDER
|
||||||
|
- _APP_SMS_FROM
|
||||||
|
- _APP_GRAPHQL_MAX_BATCH_SIZE
|
||||||
|
- _APP_GRAPHQL_MAX_COMPLEXITY
|
||||||
|
- _APP_GRAPHQL_MAX_DEPTH
|
||||||
|
- _APP_VCS_GITHUB_APP_NAME
|
||||||
|
- _APP_VCS_GITHUB_PRIVATE_KEY
|
||||||
|
- _APP_VCS_GITHUB_APP_ID
|
||||||
|
- _APP_VCS_GITHUB_WEBHOOK_SECRET
|
||||||
|
- _APP_VCS_GITHUB_CLIENT_SECRET
|
||||||
|
- _APP_VCS_GITHUB_CLIENT_ID
|
||||||
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||||
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||||
|
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||||
|
|
||||||
|
appwrite-realtime:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: realtime
|
||||||
|
container_name: appwrite-realtime
|
||||||
|
<<: *x-logging
|
||||||
|
labels:
|
||||||
|
- "traefik.constraint-label-stack=appwrite"
|
||||||
|
- "traefik.docker.network=appwrite"
|
||||||
|
- "traefik.http.services.appwrite_realtime.loadbalancer.server.port=80"
|
||||||
|
#ws
|
||||||
|
- traefik.http.routers.appwrite_realtime_ws.entrypoints=web
|
||||||
|
- traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`)
|
||||||
|
- traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime
|
||||||
|
# wss
|
||||||
|
- traefik.http.routers.appwrite_realtime_wss.entrypoints=websecure
|
||||||
|
- traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`)
|
||||||
|
- traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime
|
||||||
|
- traefik.http.routers.appwrite_realtime_wss.tls=true
|
||||||
|
- traefik.http.routers.appwrite_realtime_wss.tls.certresolver=dns
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
- redis
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_APPWRITE=/v1/realtime
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPTIONS_ABUSE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_USAGE_STATS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-audits:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-audits
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-audits
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-webhooks:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-webhooks
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-webhooks
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-deletes:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-deletes
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-deletes
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
volumes:
|
||||||
|
- appwrite-uploads:/storage/uploads:rw
|
||||||
|
- appwrite-cache:/storage/cache:rw
|
||||||
|
- appwrite-functions:/storage/functions:rw
|
||||||
|
- appwrite-builds:/storage/builds:rw
|
||||||
|
- appwrite-certificates:/storage/certificates:rw
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_STORAGE_DEVICE
|
||||||
|
- _APP_STORAGE_S3_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_S3_SECRET
|
||||||
|
- _APP_STORAGE_S3_REGION
|
||||||
|
- _APP_STORAGE_S3_BUCKET
|
||||||
|
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_DO_SPACES_SECRET
|
||||||
|
- _APP_STORAGE_DO_SPACES_REGION
|
||||||
|
- _APP_STORAGE_DO_SPACES_BUCKET
|
||||||
|
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_BACKBLAZE_SECRET
|
||||||
|
- _APP_STORAGE_BACKBLAZE_REGION
|
||||||
|
- _APP_STORAGE_BACKBLAZE_BUCKET
|
||||||
|
- _APP_STORAGE_LINODE_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_LINODE_SECRET
|
||||||
|
- _APP_STORAGE_LINODE_REGION
|
||||||
|
- _APP_STORAGE_LINODE_BUCKET
|
||||||
|
- _APP_STORAGE_WASABI_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_WASABI_SECRET
|
||||||
|
- _APP_STORAGE_WASABI_REGION
|
||||||
|
- _APP_STORAGE_WASABI_BUCKET
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_EXECUTOR_SECRET
|
||||||
|
- _APP_EXECUTOR_HOST
|
||||||
|
|
||||||
|
appwrite-worker-databases:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-databases
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-databases
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-builds:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-builds
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-builds
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
volumes:
|
||||||
|
- appwrite-functions:/storage/functions:rw
|
||||||
|
- appwrite-builds:/storage/builds:rw
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_EXECUTOR_SECRET
|
||||||
|
- _APP_EXECUTOR_HOST
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_VCS_GITHUB_APP_NAME
|
||||||
|
- _APP_VCS_GITHUB_PRIVATE_KEY
|
||||||
|
- _APP_VCS_GITHUB_APP_ID
|
||||||
|
- _APP_FUNCTIONS_TIMEOUT
|
||||||
|
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||||
|
- _APP_FUNCTIONS_CPUS
|
||||||
|
- _APP_FUNCTIONS_MEMORY
|
||||||
|
- _APP_OPTIONS_FORCE_HTTPS
|
||||||
|
- _APP_DOMAIN
|
||||||
|
- _APP_STORAGE_DEVICE
|
||||||
|
- _APP_STORAGE_S3_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_S3_SECRET
|
||||||
|
- _APP_STORAGE_S3_REGION
|
||||||
|
- _APP_STORAGE_S3_BUCKET
|
||||||
|
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_DO_SPACES_SECRET
|
||||||
|
- _APP_STORAGE_DO_SPACES_REGION
|
||||||
|
- _APP_STORAGE_DO_SPACES_BUCKET
|
||||||
|
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_BACKBLAZE_SECRET
|
||||||
|
- _APP_STORAGE_BACKBLAZE_REGION
|
||||||
|
- _APP_STORAGE_BACKBLAZE_BUCKET
|
||||||
|
- _APP_STORAGE_LINODE_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_LINODE_SECRET
|
||||||
|
- _APP_STORAGE_LINODE_REGION
|
||||||
|
- _APP_STORAGE_LINODE_BUCKET
|
||||||
|
- _APP_STORAGE_WASABI_ACCESS_KEY
|
||||||
|
- _APP_STORAGE_WASABI_SECRET
|
||||||
|
- _APP_STORAGE_WASABI_REGION
|
||||||
|
- _APP_STORAGE_WASABI_BUCKET
|
||||||
|
|
||||||
|
appwrite-worker-certificates:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-certificates
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-certificates
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
volumes:
|
||||||
|
- appwrite-config:/storage/config:rw
|
||||||
|
- appwrite-certificates:/storage/certificates:rw
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_DOMAIN
|
||||||
|
- _APP_DOMAIN_TARGET
|
||||||
|
- _APP_DOMAIN_FUNCTIONS
|
||||||
|
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-functions:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-functions
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-functions
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
- openruntimes-executor
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_FUNCTIONS_TIMEOUT
|
||||||
|
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||||
|
- _APP_FUNCTIONS_CPUS
|
||||||
|
- _APP_FUNCTIONS_MEMORY
|
||||||
|
- _APP_EXECUTOR_SECRET
|
||||||
|
- _APP_EXECUTOR_HOST
|
||||||
|
- _APP_USAGE_STATS
|
||||||
|
- _APP_DOCKER_HUB_USERNAME
|
||||||
|
- _APP_DOCKER_HUB_PASSWORD
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
|
||||||
|
appwrite-worker-mails:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-mails
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-mails
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_SYSTEM_EMAIL_NAME
|
||||||
|
- _APP_SYSTEM_EMAIL_ADDRESS
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_SMTP_HOST
|
||||||
|
- _APP_SMTP_PORT
|
||||||
|
- _APP_SMTP_SECURE
|
||||||
|
- _APP_SMTP_USERNAME
|
||||||
|
- _APP_SMTP_PASSWORD
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-messaging:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-messaging
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-messaging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_SMS_PROVIDER
|
||||||
|
- _APP_SMS_FROM
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-worker-migrations:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: worker-migrations
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-migrations
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_DOMAIN
|
||||||
|
- _APP_DOMAIN_TARGET
|
||||||
|
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||||
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||||
|
|
||||||
|
appwrite-maintenance:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: maintenance
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-maintenance
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_DOMAIN
|
||||||
|
- _APP_DOMAIN_TARGET
|
||||||
|
- _APP_DOMAIN_FUNCTIONS
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_MAINTENANCE_INTERVAL
|
||||||
|
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||||
|
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||||
|
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||||
|
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||||
|
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||||
|
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||||
|
|
||||||
|
appwrite-usage:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: usage
|
||||||
|
container_name: appwrite-usage
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- influxdb
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_INFLUXDB_HOST
|
||||||
|
- _APP_INFLUXDB_PORT
|
||||||
|
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_USAGE_STATS
|
||||||
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
|
appwrite-schedule:
|
||||||
|
<<: *x-image
|
||||||
|
entrypoint: schedule
|
||||||
|
container_name: appwrite-schedule
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
- redis
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
|
||||||
|
appwrite-assistant:
|
||||||
|
<<: *x-image-assistant
|
||||||
|
container_name: appwrite-assistant
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
environment:
|
||||||
|
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||||
|
|
||||||
|
openruntimes-executor:
|
||||||
|
container_name: openruntimes-executor
|
||||||
|
hostname: appwrite-executor
|
||||||
|
<<: *x-logging
|
||||||
|
stop_signal: SIGINT
|
||||||
|
image: openruntimes/executor:0.4.1
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
- runtimes
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- appwrite-builds:/storage/builds:rw
|
||||||
|
- appwrite-functions:/storage/functions:rw
|
||||||
|
# Host mount nessessary to share files between executor and runtimes.
|
||||||
|
# It's not possible to share mount file between 2 containers without host mount (copying is too slow)
|
||||||
|
- /tmp:/tmp:rw
|
||||||
|
environment:
|
||||||
|
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD
|
||||||
|
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL
|
||||||
|
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK
|
||||||
|
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME
|
||||||
|
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD
|
||||||
|
- OPR_EXECUTOR_ENV=$_APP_ENV
|
||||||
|
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
|
||||||
|
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
|
||||||
|
- OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
|
||||||
|
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
|
||||||
|
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
|
||||||
|
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
|
||||||
|
- OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET
|
||||||
|
- OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION
|
||||||
|
- OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET
|
||||||
|
- OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY
|
||||||
|
- OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET
|
||||||
|
- OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION
|
||||||
|
- OPR_EXECUTOR_STORAGE_DO_SPACES_BUCKET=$_APP_STORAGE_DO_SPACES_BUCKET
|
||||||
|
- OPR_EXECUTOR_STORAGE_BACKBLAZE_ACCESS_KEY=$_APP_STORAGE_BACKBLAZE_ACCESS_KEY
|
||||||
|
- OPR_EXECUTOR_STORAGE_BACKBLAZE_SECRET=$_APP_STORAGE_BACKBLAZE_SECRET
|
||||||
|
- OPR_EXECUTOR_STORAGE_BACKBLAZE_REGION=$_APP_STORAGE_BACKBLAZE_REGION
|
||||||
|
- OPR_EXECUTOR_STORAGE_BACKBLAZE_BUCKET=$_APP_STORAGE_BACKBLAZE_BUCKET
|
||||||
|
- OPR_EXECUTOR_STORAGE_LINODE_ACCESS_KEY=$_APP_STORAGE_LINODE_ACCESS_KEY
|
||||||
|
- OPR_EXECUTOR_STORAGE_LINODE_SECRET=$_APP_STORAGE_LINODE_SECRET
|
||||||
|
- OPR_EXECUTOR_STORAGE_LINODE_REGION=$_APP_STORAGE_LINODE_REGION
|
||||||
|
- OPR_EXECUTOR_STORAGE_LINODE_BUCKET=$_APP_STORAGE_LINODE_BUCKET
|
||||||
|
- OPR_EXECUTOR_STORAGE_WASABI_ACCESS_KEY=$_APP_STORAGE_WASABI_ACCESS_KEY
|
||||||
|
- OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET
|
||||||
|
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
|
||||||
|
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||||
|
container_name: appwrite-mariadb
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
volumes:
|
||||||
|
- appwrite-mariadb:/var/lib/mysql:rw
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
|
||||||
|
- MYSQL_DATABASE=${_APP_DB_SCHEMA}
|
||||||
|
- MYSQL_USER=${_APP_DB_USER}
|
||||||
|
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
||||||
|
command: 'mysqld --innodb-flush-method=fsync'
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7.0.4-alpine
|
||||||
|
container_name: appwrite-redis
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
command: >
|
||||||
|
redis-server
|
||||||
|
--maxmemory 512mb
|
||||||
|
--maxmemory-policy allkeys-lru
|
||||||
|
--maxmemory-samples 5
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
volumes:
|
||||||
|
- appwrite-redis:/data:rw
|
||||||
|
|
||||||
|
# clamav:
|
||||||
|
# image: appwrite/clamav:1.2.0
|
||||||
|
# container_name: appwrite-clamav
|
||||||
|
# restart: unless-stopped
|
||||||
|
# networks:
|
||||||
|
# - appwrite
|
||||||
|
# volumes:
|
||||||
|
# - appwrite-uploads:/storage/uploads
|
||||||
|
|
||||||
|
influxdb:
|
||||||
|
image: appwrite/influxdb:1.5.0
|
||||||
|
container_name: appwrite-influxdb
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
volumes:
|
||||||
|
- appwrite-influxdb:/var/lib/influxdb:rw
|
||||||
|
|
||||||
|
telegraf:
|
||||||
|
image: appwrite/telegraf:1.4.0
|
||||||
|
container_name: appwrite-telegraf
|
||||||
|
<<: *x-logging
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
environment:
|
||||||
|
- _APP_INFLUXDB_HOST
|
||||||
|
- _APP_INFLUXDB_PORT
|
||||||
|
|
||||||
|
networks:
|
||||||
|
gateway:
|
||||||
|
name: gateway
|
||||||
|
appwrite:
|
||||||
|
name: appwrite
|
||||||
|
runtimes:
|
||||||
|
name: runtimes
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
appwrite-mariadb:
|
||||||
|
appwrite-redis:
|
||||||
|
appwrite-cache:
|
||||||
|
appwrite-uploads:
|
||||||
|
appwrite-certificates:
|
||||||
|
appwrite-functions:
|
||||||
|
appwrite-builds:
|
||||||
|
appwrite-influxdb:
|
||||||
|
appwrite-config:
|
||||||
27
examples/compose/fider.yaml
Normal file
27
examples/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}
|
||||||
29
examples/compose/ghost.yaml
Normal file
29
examples/compose/ghost.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:5
|
||||||
|
volumes:
|
||||||
|
- ghost-content-data:/var/lib/ghost/content
|
||||||
|
environment:
|
||||||
|
- url=$SERVICE_FQDN_GHOST
|
||||||
|
- database__client=mysql
|
||||||
|
- database__connection__host=mysql
|
||||||
|
- database__connection__user=$SERVICE_USER_MYSQL
|
||||||
|
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
||||||
|
- database__connection__database=${MYSQL_DATABASE-ghost}
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
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}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
16
examples/compose/minio.yaml
Normal file
16
examples/compose/minio.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: quay.io/minio/minio:RELEASE.2023-09-30T07-02-29Z
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
environment:
|
||||||
|
SERVICE_FQDN_MINIO_9000:
|
||||||
|
SERVICE_FQDN_CONSOLE_9001:
|
||||||
|
MINIO_ROOT_USER: $SERVICE_USER_MINIO
|
||||||
|
MINIO_ROOT_PASSWORD: $SERVICE_PASSWORD_MINIO
|
||||||
|
volumes:
|
||||||
|
- minio-data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
52
examples/compose/plausible.yaml
Normal file
52
examples/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/compose/postgres.yaml
Normal file
15
examples/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/compose/uptime-kuma.yaml
Normal file
5
examples/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/compose/weird.yaml
Normal file
76
examples/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:
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
services:
|
|
||||||
ghost:
|
|
||||||
documentation: https://ghost.org/docs/config
|
|
||||||
image: ghost:5
|
|
||||||
volumes:
|
|
||||||
- ghost-content-data:/var/lib/ghost/content
|
|
||||||
- type: volume
|
|
||||||
source: /data/g
|
|
||||||
target: /data
|
|
||||||
volume:
|
|
||||||
nocopy: true
|
|
||||||
environment:
|
|
||||||
- url=$SERVICE_FQDN_GHOST
|
|
||||||
- database__client=mysql
|
|
||||||
- database__connection__host=mysql
|
|
||||||
- database__connection__user=$SERVICE_USER_MYSQL
|
|
||||||
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
|
||||||
- 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:
|
|
||||||
- mysql
|
|
||||||
mysql:
|
|
||||||
documentation: https://hub.docker.com/_/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_MYSQL_ROOT}
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
ipam:
|
|
||||||
driver: default
|
|
||||||
config:
|
|
||||||
- subnet: "172.16.238.0/24"
|
|
||||||
- subnet: "2001:3984:3989::/64"
|
|
||||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user