Merge pull request #1516 from coollabsio/next

v4.0.0-beta.152
This commit is contained in:
Andras Bacsai
2023-12-04 11:26:17 +01:00
committed by GitHub
20 changed files with 173 additions and 138 deletions

View File

@@ -21,7 +21,7 @@ class StartService
$commands[] = "echo 'Pulling images.'"; $commands[] = "echo 'Pulling images.'";
$commands[] = "docker compose pull"; $commands[] = "docker compose pull";
$commands[] = "echo 'Starting containers.'"; $commands[] = "echo 'Starting containers.'";
$commands[] = "docker compose up -d --remove-orphans --force-recreate"; $commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true"; $commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
$compose = data_get($service,'docker_compose',[]); $compose = data_get($service,'docker_compose',[]);
$serviceNames = data_get(Yaml::parse($compose),'services',[]); $serviceNames = data_get(Yaml::parse($compose),'services',[]);

View File

@@ -32,6 +32,7 @@ class Index extends Component
public ?int $remoteServerPort = 22; public ?int $remoteServerPort = 22;
public ?string $remoteServerUser = 'root'; public ?string $remoteServerUser = 'root';
public bool $isSwarmManager = false; public bool $isSwarmManager = false;
public bool $isCloudflareTunnel = false;
public ?Server $createdServer = null; public ?Server $createdServer = null;
public Collection $projects; public Collection $projects;
@@ -184,6 +185,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
'team_id' => currentTeam()->id, 'team_id' => currentTeam()->id,
]); ]);
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager; $this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
$this->createdServer->settings->save(); $this->createdServer->settings->save();
$this->createdServer->addInitialNetwork(); $this->createdServer->addInitialNetwork();
$this->validateServer(); $this->validateServer();
@@ -200,6 +202,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
]); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->serverReachable = false; $this->serverReachable = false;
$this->createdServer->delete();
return handleError(error: $e, livewire: $this); return handleError(error: $e, livewire: $this);
} }

View File

@@ -22,79 +22,19 @@ class DockerCompose extends Component
$this->query = request()->query(); $this->query = request()->query();
if (isDev()) { if (isDev()) {
$this->dockerComposeRaw = 'services: $this->dockerComposeRaw = 'services:
ghost: appsmith:
image: ghost:5 build:
volumes: context: .
- ~/configs:/etc/configs/:ro dockerfile_inline: |
- ./var/lib/ghost/content:/tmp/ghost2/content:ro FROM nginx
- /var/lib/ghost/content:/tmp/ghost/content:rw ARG GIT_COMMIT
- ghost-content-data:/var/lib/ghost/content ARG GIT_BRANCH
- type: volume RUN echo "Hello World ${GIT_COMMIT} ${GIT_BRANCH}"
source: mydata args:
target: /data - GIT_COMMIT=cdc3b19
- type: bind - GIT_BRANCH=${GIT_BRANCH}
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: environment:
- database__client=${DATABASE_CLIENT:-mysql} - APPSMITH_MAIL_ENABLED=${APPSMITH_MAIL_ENABLED}
- 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:
'; ';
} }
} }

View File

@@ -59,7 +59,6 @@ class Show extends Component
{ {
$this->validate(); $this->validate();
$this->env->save(); $this->env->save();
ray($this->env);
$this->emit('success', 'Environment variable updated successfully.'); $this->emit('success', 'Environment variable updated successfully.');
$this->emit('refreshEnvs'); $this->emit('refreshEnvs');
} }

View File

@@ -14,6 +14,9 @@ class Status extends Component
public int $numberOfPolls = 0; public int $numberOfPolls = 0;
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling']; protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
public function mount() {
$this->checkProxy();
}
public function startProxyPolling() public function startProxyPolling()
{ {
$this->checkProxy(); $this->checkProxy();

View File

@@ -347,6 +347,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->generate_image_names(); $this->generate_image_names();
$this->check_image_locally_or_remotely(); $this->check_image_locally_or_remotely();
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) { if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
$this->create_workdir();
$this->generate_compose_file(); $this->generate_compose_file();
$this->rolling_update(); $this->rolling_update();
return; return;
@@ -442,10 +443,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->clone_repository(); $this->clone_repository();
$this->generate_image_names(); $this->generate_image_names();
$this->cleanup_git(); $this->cleanup_git();
$this->application->loadComposeFile(isInit: false);
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id); $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
$yaml = Yaml::dump($composeFile->toArray(), 10); $yaml = Yaml::dump($composeFile->toArray(), 10);
ray($composeFile);
ray($this->container_name);
$this->docker_compose_base64 = base64_encode($yaml); $this->docker_compose_base64 = base64_encode($yaml);
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
@@ -453,12 +453,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->save_environment_variables(); $this->save_environment_variables();
$this->stop_running_container(force: true); $this->stop_running_container(force: true);
ray($this->pull_request_id);
$networkId = $this->application->uuid; $networkId = $this->application->uuid;
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
$networkId = "{$this->application->uuid}-{$this->pull_request_id}"; $networkId = "{$this->application->uuid}-{$this->pull_request_id}";
} }
ray($networkId);
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
// TODO // TODO
} else { } else {
@@ -487,7 +485,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
); );
} }
$this->start_by_compose_file(); $this->start_by_compose_file();
$this->application->loadComposeFile(isInit: false);
} }
private function deploy_dockerfile_buildpack() private function deploy_dockerfile_buildpack()
{ {
@@ -530,6 +527,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if (!$this->force_rebuild) { if (!$this->force_rebuild) {
$this->check_image_locally_or_remotely(); $this->check_image_locally_or_remotely();
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) { if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
$this->create_workdir();
$this->execute_remote_command([ $this->execute_remote_command([
"echo 'No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.'", "echo 'No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.'",
]); ]);
@@ -677,7 +675,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true], [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
); );
} }
private function create_workdir()
{
$this->execute_remote_command(
[
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
],
);
}
private function prepare_builder_image() private function prepare_builder_image()
{ {
$helperImage = config('coolify.helper_image'); $helperImage = config('coolify.helper_image');
@@ -686,9 +691,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server); $this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
if ($this->dockerConfigFileExists === 'OK') { if ($this->dockerConfigFileExists === 'OK') {
$runCommand = "docker run -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
} else { } else {
$runCommand = "docker run -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 -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
} }
$this->execute_remote_command( $this->execute_remote_command(
[ [
@@ -703,13 +708,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
], ],
); );
if ($this->restart_only || !$this->force_rebuild) {
$this->execute_remote_command(
[
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
],
);
}
} }
private function deploy_to_additional_destinations() private function deploy_to_additional_destinations()
{ {
@@ -1250,10 +1248,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true], [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true], [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
); );
} else if ($this->application->build_pack === 'dockercompose') {
$this->execute_remote_command(
["docker compose --project-directory {$this->configuration_dir} up --build -d", "hidden" => true],
);
} else { } else {
$this->execute_remote_command( $this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true], [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],

View File

@@ -34,6 +34,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
public function __construct(public Server $server) public function __construct(public Server $server)
{ {
if (isDev()) $this->handle();
} }

View File

@@ -587,12 +587,12 @@ class Application extends BaseModel
$commands = collect([]); $commands = collect([]);
if ($dockerConfigFileExists === 'OK') { if ($dockerConfigFileExists === 'OK') {
$commands->push([ $commands->push([
"command" => "docker run -d --network $network -v /:/host --name $deploymentUuid --rm -v {$serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock $helperImage", "command" => "docker run -d --network $network --name $deploymentUuid --rm -v {$serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock $helperImage",
"hidden" => true, "hidden" => true,
]); ]);
} else { } else {
$commands->push([ $commands->push([
"command" => "docker run -d --network {$network} -v /:/host --name {$deploymentUuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}", "command" => "docker run -d --network {$network} --name {$deploymentUuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}",
"hidden" => true, "hidden" => true,
]); ]);
} }
@@ -606,7 +606,6 @@ class Application extends BaseModel
{ {
if ($this->docker_compose_raw) { if ($this->docker_compose_raw) {
$mainCompose = parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id); $mainCompose = parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id);
ray($this->docker_compose_pr_raw);
if ($this->getMorphClass() === 'App\Models\Application' && $this->docker_compose_pr_raw) { if ($this->getMorphClass() === 'App\Models\Application' && $this->docker_compose_pr_raw) {
parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, is_pr: true); parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, is_pr: true);
} }

View File

@@ -87,8 +87,6 @@ class Server extends BaseModel
return $this->hasOne(ServerSetting::class); return $this->hasOne(ServerSetting::class);
} }
public function addInitialNetwork() { public function addInitialNetwork() {
ray($this->id);
if ($this->id === 0) { if ($this->id === 0) {
if ($this->isSwarm()) { if ($this->isSwarm()) {
SwarmDocker::create([ SwarmDocker::create([

View File

@@ -131,6 +131,20 @@ class Service extends BaseModel
} }
$fields->put('Weblate', $data); $fields->put('Weblate', $data);
break; break;
case str($image)?->contains('meilisearch'):
$data = collect([]);
$SERVICE_PASSWORD_MEILISEARCH = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_MEILISEARCH')->first();
if ($SERVICE_PASSWORD_MEILISEARCH) {
$data = $data->merge([
'API Key' => [
'key' => data_get($SERVICE_PASSWORD_MEILISEARCH, 'key'),
'value' => data_get($SERVICE_PASSWORD_MEILISEARCH, 'value'),
'isPassword' => true,
],
]);
}
$fields->put('Meilisearch', $data);
break;
case str($image)?->contains('ghost'): case str($image)?->contains('ghost'):
$data = collect([]); $data = collect([]);
$MAIL_OPTIONS_AUTH_PASS = $this->environment_variables()->where('key', 'MAIL_OPTIONS_AUTH_PASS')->first(); $MAIL_OPTIONS_AUTH_PASS = $this->environment_variables()->where('key', 'MAIL_OPTIONS_AUTH_PASS')->first();
@@ -193,6 +207,7 @@ class Service extends BaseModel
break; break;
} }
} }
ray($fields);
$databases = $this->databases()->get(); $databases = $this->databases()->get();
foreach ($databases as $database) { foreach ($databases as $database) {

View File

@@ -97,12 +97,12 @@ function prepareHelperContainer(Server $server, string $network, string $deploym
$commands = collect([]); $commands = collect([]);
if ($dockerConfigFileExists === 'OK') { if ($dockerConfigFileExists === 'OK') {
$commands->push([ $commands->push([
"command" => "docker run -d --network $network -v /:/host --name $deploymentUuid --rm -v {$serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock $helperImage", "command" => "docker run -d --network $network --name $deploymentUuid --rm -v {$serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock $helperImage",
"hidden" => true, "hidden" => true,
]); ]);
} else { } else {
$commands->push([ $commands->push([
"command" => "docker run -d --network {$network} -v /:/host --name {$deploymentUuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}", "command" => "docker run -d --network {$network} --name {$deploymentUuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}",
"hidden" => true, "hidden" => true,
]); ]);
} }

View File

@@ -1003,7 +1003,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($savedService->serviceType()) { if ($savedService->serviceType()) {
$fqdns = generateServiceSpecificFqdns($savedService, forTraefik: true); $fqdns = generateServiceSpecificFqdns($savedService, forTraefik: true);
} else { } else {
$fqdns = collect(data_get($savedService, 'fqdns')); $fqdns = collect(data_get($savedService, 'fqdns'))->filter();
} }
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id); $defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
$serviceLabels = $serviceLabels->merge($defaultLabels); $serviceLabels = $serviceLabels->merge($defaultLabels);
@@ -1102,6 +1102,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$serviceNetworks = collect(data_get($service, 'networks', [])); $serviceNetworks = collect(data_get($service, 'networks', []));
$serviceVariables = collect(data_get($service, 'environment', [])); $serviceVariables = collect(data_get($service, 'environment', []));
$serviceLabels = collect(data_get($service, 'labels', [])); $serviceLabels = collect(data_get($service, 'labels', []));
$serviceBuildVariables = collect(data_get($service, 'build.args', []));
$serviceVariables = $serviceVariables->merge($serviceBuildVariables);
if ($serviceLabels->count() > 0) { if ($serviceLabels->count() > 0) {
$removedLabels = collect([]); $removedLabels = collect([]);
$serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) { $serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) {
@@ -1148,7 +1150,52 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_set($service, 'volumes', $serviceVolumes->toArray()); data_set($service, 'volumes', $serviceVolumes->toArray());
} }
} else { } else {
// TODO if (count($serviceVolumes) > 0) {
$serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes) {
if (is_string($volume)) {
$volume = str($volume);
if ($volume->contains(':')) {
$name = $volume->before(':');
$mount = $volume->after(':');
if ($name->startsWith('.') || $name->startsWith('~')) {
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
if ($name->startsWith('.')) {
$name = $name->replaceFirst('.', $dir);
}
if ($name->startsWith('~')) {
$name = $name->replaceFirst('~', $dir);
}
$volume = str("$name:$mount");
} else {
$topLevelVolumes->put($name->value(), [
'name' => $name->value(),
]);
}
}
} else if (is_array($volume)) {
$source = data_get($volume, 'source');
if ($source) {
if (str($source, '.') || str($source, '~')) {
$dir = base_configuration_dir() . '/applications/' . $resource->uuid;
if (str($source, '.')) {
$source = str('.', $dir, $source);
}
if (str($source, '~')) {
$source = str('~', $dir, $source);
}
data_set($volume, 'source', $source);
} else {
data_set($volume, 'source', $source);
$topLevelVolumes->put($source, [
'name' => $source,
]);
}
}
}
return $volume->value();
});
data_set($service, 'volumes', $serviceVolumes->toArray());
}
} }
// Decide if the service is a database // Decide if the service is a database
$isDatabase = isDatabaseImage(data_get_str($service, 'image')); $isDatabase = isDatabaseImage(data_get_str($service, 'image'));

View File

@@ -7,7 +7,7 @@ return [
// 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.151', 'release' => '4.0.0-beta.152',
// 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'),

View File

@@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.151'; return '4.0.0-beta.152';

View File

@@ -207,10 +207,11 @@
placeholder="Username to connect to your server. Default is root." label="Username" placeholder="Username to connect to your server. Default is root." label="Username"
id="remoteServerUser" /> id="remoteServerUser" />
</div> </div>
{{-- <div class="w-64"> <div class="w-64">
<x-forms.checkbox type="checkbox" id="isSwarmManager" <x-forms.checkbox
label="Is it a Swarm Manager?" /> helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
</div> --}} id="isCloudflareTunnel" label="Cloudflare Tunnel" />
</div>
<x-forms.button type="submit">Check Connection</x-forms.button> <x-forms.button type="submit">Check Connection</x-forms.button>
</form> </form>
</x-slot:actions> </x-slot:actions>

View File

@@ -9,33 +9,36 @@
helper="See details in our <a target='_blank' class='text-white underline' href='https://coolify.io/docs/api-authentication'>documentation</a>." helper="See details in our <a target='_blank' class='text-white underline' href='https://coolify.io/docs/api-authentication'>documentation</a>."
label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input> label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input>
</div> </div>
<div> @if ($resource->type() !== 'service')
<h3>Manual Git Webhooks</h3> <div>
@if ($githubManualWebhook && $gitlabManualWebhook) <h3>Manual Git Webhooks</h3>
<form wire:submit.prevent='saveSecret' class="flex flex-col gap-2"> @if ($githubManualWebhook && $gitlabManualWebhook)
<div class="flex items-end gap-2"> <form wire:submit.prevent='saveSecret' class="flex flex-col gap-2">
<x-forms.input helper="Content Type in GitHub configuration could be json or form-urlencoded." <div class="flex items-end gap-2">
readonly label="GitHub" id="githubManualWebhook"></x-forms.input> <x-forms.input helper="Content Type in GitHub configuration could be json or form-urlencoded."
<x-forms.input type="password" readonly label="GitHub" id="githubManualWebhook"></x-forms.input>
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitHub." <x-forms.input type="password"
label="GitHub Webhook Secret" id="resource.manual_webhook_secret_github"></x-forms.input> helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitHub."
label="GitHub Webhook Secret" id="resource.manual_webhook_secret_github"></x-forms.input>
</div>
<a target="_blank" class="flex hover:no-underline" href="{{ $resource?->gitWebhook }}">
<x-forms.button>Webhook Configuration on GitHub
<x-external-link />
</x-forms.button>
</a>
<div class="flex gap-2">
<x-forms.input readonly label="GitLab" id="gitlabManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitLab."
label="GitLab Webhook Secret" id="resource.manual_webhook_secret_gitlab"></x-forms.input>
</div>
<x-forms.button type="submit">Save</x-forms.button>
</form>
@else
You are using an official Git App. You do not need manual webhooks.
@endif
</div>
@endif
</div>
<a target="_blank" class="flex hover:no-underline" href="{{ $resource?->gitWebhook }}">
<x-forms.button>Webhook Configuration on GitHub
<x-external-link />
</x-forms.button>
</a>
<div class="flex gap-2">
<x-forms.input readonly label="GitLab" id="gitlabManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitLab."
label="GitLab Webhook Secret" id="resource.manual_webhook_secret_gitlab"></x-forms.input>
</div>
<x-forms.button type="submit">Save</x-forms.button>
</form>
@else
You are using an official Git App. You do not need manual webhooks.
@endif
</div>
</div> </div>

View File

@@ -1,6 +1,6 @@
<div> <div>
@if ($server->isFunctional()) @if ($server->isFunctional())
<div class="flex gap-2" wire:poll.5000ms='checkProxy'> <div class="flex gap-2">
@if (data_get($server, 'proxy.status') === 'running') @if (data_get($server, 'proxy.status') === 'running')
<x-status.running status="Proxy Running" /> <x-status.running status="Proxy Running" />
@elseif (data_get($server, 'proxy.status') === 'restarting') @elseif (data_get($server, 'proxy.status') === 'restarting')

View File

@@ -0,0 +1,19 @@
# documentation: https://www.meilisearch.com/docs/learn/configuration/instance_options
# slogan: MeiliSearch is a powerful, fast, open-source, easy to use and deploy search engine.
# tags: search,engine,fulltext,full,text,meilisearch
services:
meilisearch:
image: getmeili/meilisearch:latest
environment:
- SERVICE_FQDN_MEILISEARCH
- MEILI_NO_ANALYTICS=${MEILI_NO_ANALYTICS:-true}
- MEILI_ENV=${MEILI_ENV:-production}
- MEILI_MASTER_KEY=${SERVICE_PASSWORD_MEILISEARCH}
volumes:
- meilisearch-data:/meili_data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7700/health"]
interval: 2s
timeout: 10s
retries: 15

View File

@@ -304,6 +304,19 @@
"low-code" "low-code"
] ]
}, },
"meilisearch": {
"documentation": "https:\/\/www.meilisearch.com\/docs\/learn\/configuration\/instance_options",
"slogan": "MeiliSearch is a powerful, fast, open-source, easy to use and deploy search engine.",
"compose": "c2VydmljZXM6CiAgbWVpbGlzZWFyY2g6CiAgICBpbWFnZTogJ2dldG1laWxpL21laWxpc2VhcmNoOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRUlMSVNFQVJDSAogICAgICAtICdNRUlMSV9OT19BTkFMWVRJQ1M9JHtNRUlMSV9OT19BTkFMWVRJQ1M6LXRydWV9JwogICAgICAtICdNRUlMSV9FTlY9JHtNRUlMSV9FTlY6LXByb2R1Y3Rpb259JwogICAgICAtICdNRUlMSV9NQVNURVJfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NRUlMSVNFQVJDSH0nCiAgICB2b2x1bWVzOgogICAgICAtICdtZWlsaXNlYXJjaC1kYXRhOi9tZWlsaV9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0Ojc3MDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
"tags": [
"search",
"engine",
"fulltext",
"full",
"text",
"meilisearch"
]
},
"metube": { "metube": {
"documentation": "https:\/\/github.com\/alexta69\/metube", "documentation": "https:\/\/github.com\/alexta69\/metube",
"slogan": "A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.", "slogan": "A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.",

View File

@@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.151" "version": "4.0.0-beta.152"
} }
} }
} }