@@ -217,6 +217,10 @@ class StartMongodb
|
|||||||
if ($this->database->enable_ssl) {
|
if ($this->database->enable_ssl) {
|
||||||
$commandParts = ['mongod'];
|
$commandParts = ['mongod'];
|
||||||
|
|
||||||
|
if (! empty($this->database->mongo_conf)) {
|
||||||
|
$commandParts = ['mongod', '--config', '/etc/mongo/mongod.conf'];
|
||||||
|
}
|
||||||
|
|
||||||
$sslConfig = match ($this->database->ssl_mode) {
|
$sslConfig = match ($this->database->ssl_mode) {
|
||||||
'allow' => [
|
'allow' => [
|
||||||
'--tlsMode=allowTLS',
|
'--tlsMode=allowTLS',
|
||||||
|
@@ -14,15 +14,26 @@ class CleanupDocker
|
|||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
$settings = instanceSettings();
|
$settings = instanceSettings();
|
||||||
|
$realtimeImage = config('constants.coolify.realtime_image');
|
||||||
|
$realtimeImageVersion = config('constants.coolify.realtime_version');
|
||||||
|
$realtimeImageWithVersion = "$realtimeImage:$realtimeImageVersion";
|
||||||
|
$realtimeImageWithoutPrefix = 'coollabsio/coolify-realtime';
|
||||||
|
$realtimeImageWithoutPrefixVersion = "coollabsio/coolify-realtime:$realtimeImageVersion";
|
||||||
|
|
||||||
$helperImageVersion = data_get($settings, 'helper_version');
|
$helperImageVersion = data_get($settings, 'helper_version');
|
||||||
$helperImage = config('constants.coolify.helper_image');
|
$helperImage = config('constants.coolify.helper_image');
|
||||||
$helperImageWithVersion = "$helperImage:$helperImageVersion";
|
$helperImageWithVersion = "$helperImage:$helperImageVersion";
|
||||||
|
$helperImageWithoutPrefix = 'coollabsio/coolify-helper';
|
||||||
|
$helperImageWithoutPrefixVersion = "coollabsio/coolify-helper:$helperImageVersion";
|
||||||
|
|
||||||
$commands = [
|
$commands = [
|
||||||
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',
|
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',
|
||||||
'docker image prune -af --filter "label!=coolify.managed=true"',
|
'docker image prune -af --filter "label!=coolify.managed=true"',
|
||||||
'docker builder prune -af',
|
'docker builder prune -af',
|
||||||
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi -f",
|
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi -f",
|
||||||
|
"docker images --filter before=$realtimeImageWithVersion --filter reference=$realtimeImage | grep $realtimeImage | awk '{print $3}' | xargs -r docker rmi -f",
|
||||||
|
"docker images --filter before=$helperImageWithoutPrefixVersion --filter reference=$helperImageWithoutPrefix | grep $helperImageWithoutPrefix | awk '{print $3}' | xargs -r docker rmi -f",
|
||||||
|
"docker images --filter before=$realtimeImageWithoutPrefixVersion --filter reference=$realtimeImageWithoutPrefix | grep $realtimeImageWithoutPrefix | awk '{print $3}' | xargs -r docker rmi -f",
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($server->settings->delete_unused_volumes) {
|
if ($server->settings->delete_unused_volumes) {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands\Generate;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
@@ -8,7 +8,7 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
|
|
||||||
class OpenApi extends Command
|
class OpenApi extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'openapi';
|
protected $signature = 'generate:openapi';
|
||||||
|
|
||||||
protected $description = 'Generate OpenApi file.';
|
protected $description = 'Generate OpenApi file.';
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ class OpenApi extends Command
|
|||||||
echo "Generating OpenAPI documentation.\n";
|
echo "Generating OpenAPI documentation.\n";
|
||||||
// https://github.com/OAI/OpenAPI-Specification/releases
|
// https://github.com/OAI/OpenAPI-Specification/releases
|
||||||
$process = Process::run([
|
$process = Process::run([
|
||||||
'/var/www/html/vendor/bin/openapi',
|
'./vendor/bin/openapi',
|
||||||
'app',
|
'app',
|
||||||
'-o',
|
'-o',
|
||||||
'openapi.yaml',
|
'openapi.yaml',
|
@@ -1,17 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands\Generate;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class ServicesGenerate extends Command
|
class Services extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
protected $signature = 'services:generate';
|
protected $signature = 'generate:services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
@@ -880,12 +880,17 @@ class ApplicationsController extends Controller
|
|||||||
if ($instantDeploy) {
|
if ($instantDeploy) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
LoadComposeFile::dispatch($application);
|
LoadComposeFile::dispatch($application);
|
||||||
@@ -1004,12 +1009,17 @@ class ApplicationsController extends Controller
|
|||||||
if ($instantDeploy) {
|
if ($instantDeploy) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
LoadComposeFile::dispatch($application);
|
LoadComposeFile::dispatch($application);
|
||||||
@@ -1101,12 +1111,17 @@ class ApplicationsController extends Controller
|
|||||||
if ($instantDeploy) {
|
if ($instantDeploy) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
LoadComposeFile::dispatch($application);
|
LoadComposeFile::dispatch($application);
|
||||||
@@ -1190,12 +1205,17 @@ class ApplicationsController extends Controller
|
|||||||
if ($instantDeploy) {
|
if ($instantDeploy) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(serializeApiResponse([
|
return response()->json(serializeApiResponse([
|
||||||
@@ -1254,12 +1274,17 @@ class ApplicationsController extends Controller
|
|||||||
if ($instantDeploy) {
|
if ($instantDeploy) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(serializeApiResponse([
|
return response()->json(serializeApiResponse([
|
||||||
@@ -1610,6 +1635,18 @@ class ApplicationsController extends Controller
|
|||||||
['bearerAuth' => []],
|
['bearerAuth' => []],
|
||||||
],
|
],
|
||||||
tags: ['Applications'],
|
tags: ['Applications'],
|
||||||
|
parameters: [
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'uuid',
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the application.',
|
||||||
|
required: true,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'string',
|
||||||
|
format: 'uuid',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
requestBody: new OA\RequestBody(
|
requestBody: new OA\RequestBody(
|
||||||
description: 'Application updated.',
|
description: 'Application updated.',
|
||||||
required: true,
|
required: true,
|
||||||
@@ -1884,11 +1921,16 @@ class ApplicationsController extends Controller
|
|||||||
if ($instantDeploy) {
|
if ($instantDeploy) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -2705,13 +2747,21 @@ class ApplicationsController extends Controller
|
|||||||
|
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: $force,
|
force_rebuild: $force,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
no_questions_asked: $instant_deploy
|
no_questions_asked: $instant_deploy
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json(
|
||||||
|
[
|
||||||
|
'message' => $result['message'],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json(
|
return response()->json(
|
||||||
[
|
[
|
||||||
@@ -2866,12 +2916,17 @@ class ApplicationsController extends Controller
|
|||||||
|
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
|
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
restart_only: true,
|
restart_only: true,
|
||||||
is_api: true,
|
is_api: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => $result['message'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json(
|
return response()->json(
|
||||||
[
|
[
|
||||||
@@ -3006,73 +3061,73 @@ class ApplicationsController extends Controller
|
|||||||
// ]);
|
// ]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private function validateDataApplications(Request $request, Server $server)
|
private function validateDataApplications(Request $request, Server $server)
|
||||||
{
|
{
|
||||||
$teamId = getTeamIdFromToken();
|
$teamId = getTeamIdFromToken();
|
||||||
|
|
||||||
// Validate ports_mappings
|
// Validate ports_mappings
|
||||||
if ($request->has('ports_mappings')) {
|
if ($request->has('ports_mappings')) {
|
||||||
$ports = [];
|
$ports = [];
|
||||||
foreach (explode(',', $request->ports_mappings) as $portMapping) {
|
foreach (explode(',', $request->ports_mappings) as $portMapping) {
|
||||||
$port = explode(':', $portMapping);
|
$port = explode(':', $portMapping);
|
||||||
if (in_array($port[0], $ports)) {
|
if (in_array($port[0], $ports)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Validation failed.',
|
'message' => 'Validation failed.',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'ports_mappings' => 'The first number before : should be unique between mappings.',
|
'ports_mappings' => 'The first number before : should be unique between mappings.',
|
||||||
],
|
],
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
$ports[] = $port[0];
|
$ports[] = $port[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Validate custom_labels
|
// Validate custom_labels
|
||||||
if ($request->has('custom_labels')) {
|
if ($request->has('custom_labels')) {
|
||||||
if (! isBase64Encoded($request->custom_labels)) {
|
if (! isBase64Encoded($request->custom_labels)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Validation failed.',
|
'message' => 'Validation failed.',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'custom_labels' => 'The custom_labels should be base64 encoded.',
|
'custom_labels' => 'The custom_labels should be base64 encoded.',
|
||||||
],
|
],
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
$customLabels = base64_decode($request->custom_labels);
|
$customLabels = base64_decode($request->custom_labels);
|
||||||
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
|
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Validation failed.',
|
'message' => 'Validation failed.',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'custom_labels' => 'The custom_labels should be base64 encoded.',
|
'custom_labels' => 'The custom_labels should be base64 encoded.',
|
||||||
],
|
],
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($request->has('domains') && $server->isProxyShouldRun()) {
|
if ($request->has('domains') && $server->isProxyShouldRun()) {
|
||||||
$uuid = $request->uuid;
|
$uuid = $request->uuid;
|
||||||
$fqdn = $request->domains;
|
$fqdn = $request->domains;
|
||||||
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
|
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
|
||||||
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
|
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
|
||||||
$errors = [];
|
$errors = [];
|
||||||
$fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) {
|
$fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) {
|
||||||
if (filter_var($domain, FILTER_VALIDATE_URL) === false) {
|
if (filter_var($domain, FILTER_VALIDATE_URL) === false) {
|
||||||
$errors[] = 'Invalid domain: '.$domain;
|
$errors[] = 'Invalid domain: '.$domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
return str($domain)->trim()->lower();
|
return str($domain)->trim()->lower();
|
||||||
});
|
});
|
||||||
if (count($errors) > 0) {
|
if (count($errors) > 0) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Validation failed.',
|
'message' => 'Validation failed.',
|
||||||
'errors' => $errors,
|
'errors' => $errors,
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
if (checkIfDomainIsAlreadyUsed($fqdn, $teamId, $uuid)) {
|
if (checkIfDomainIsAlreadyUsed($fqdn, $teamId, $uuid)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Validation failed.',
|
'message' => 'Validation failed.',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'domains' => 'One of the domain is already used.',
|
'domains' => 'One of the domain is already used.',
|
||||||
],
|
],
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ use App\Http\Controllers\Controller;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
use App\Models\Tag;
|
use App\Models\Tag;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
@@ -132,7 +133,7 @@ class DeployController extends Controller
|
|||||||
|
|
||||||
#[OA\Get(
|
#[OA\Get(
|
||||||
summary: 'Deploy',
|
summary: 'Deploy',
|
||||||
description: 'Deploy by tag or uuid. `Post` request also accepted.',
|
description: 'Deploy by tag or uuid. `Post` request also accepted with `uuid` and `tag` json body.',
|
||||||
path: '/deploy',
|
path: '/deploy',
|
||||||
operationId: 'deploy-by-tag-or-uuid',
|
operationId: 'deploy-by-tag-or-uuid',
|
||||||
security: [
|
security: [
|
||||||
@@ -191,10 +192,10 @@ class DeployController extends Controller
|
|||||||
return invalidTokenResponse();
|
return invalidTokenResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
$uuids = $request->query->get('uuid');
|
$uuids = $request->input('uuid');
|
||||||
$tags = $request->query->get('tag');
|
$tags = $request->input('tag');
|
||||||
$force = $request->query->get('force') ?? false;
|
$force = $request->input('force') ?? false;
|
||||||
$pr = $request->query->get('pr') ? max((int) $request->query->get('pr'), 0) : 0;
|
$pr = $request->input('pr') ? max((int) $request->input('pr'), 0) : 0;
|
||||||
|
|
||||||
if ($uuids && $tags) {
|
if ($uuids && $tags) {
|
||||||
return response()->json(['message' => 'You can only use uuid or tag, not both.'], 400);
|
return response()->json(['message' => 'You can only use uuid or tag, not both.'], 400);
|
||||||
@@ -297,17 +298,21 @@ class DeployController extends Controller
|
|||||||
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
|
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
|
||||||
}
|
}
|
||||||
switch ($resource?->getMorphClass()) {
|
switch ($resource?->getMorphClass()) {
|
||||||
case \App\Models\Application::class:
|
case Application::class:
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $resource,
|
application: $resource,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: $force,
|
force_rebuild: $force,
|
||||||
pull_request_id: $pr,
|
pull_request_id: $pr,
|
||||||
);
|
);
|
||||||
$message = "Application {$resource->name} deployment queued.";
|
if ($result['status'] === 'skipped') {
|
||||||
|
$message = $result['message'];
|
||||||
|
} else {
|
||||||
|
$message = "Application {$resource->name} deployment queued.";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case \App\Models\Service::class:
|
case Service::class:
|
||||||
StartService::run($resource);
|
StartService::run($resource);
|
||||||
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
||||||
break;
|
break;
|
||||||
@@ -333,6 +338,40 @@ class DeployController extends Controller
|
|||||||
['bearerAuth' => []],
|
['bearerAuth' => []],
|
||||||
],
|
],
|
||||||
tags: ['Deployments'],
|
tags: ['Deployments'],
|
||||||
|
parameters: [
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'uuid',
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the application.',
|
||||||
|
required: true,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'string',
|
||||||
|
format: 'uuid',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'skip',
|
||||||
|
in: 'query',
|
||||||
|
description: 'Number of records to skip.',
|
||||||
|
required: false,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
default: 0,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'take',
|
||||||
|
in: 'query',
|
||||||
|
description: 'Number of records to take.',
|
||||||
|
required: false,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 1,
|
||||||
|
default: 10,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
responses: [
|
responses: [
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 200,
|
response: 200,
|
||||||
|
@@ -267,6 +267,18 @@ class ProjectController extends Controller
|
|||||||
['bearerAuth' => []],
|
['bearerAuth' => []],
|
||||||
],
|
],
|
||||||
tags: ['Projects'],
|
tags: ['Projects'],
|
||||||
|
parameters: [
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'uuid',
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the project.',
|
||||||
|
required: true,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'string',
|
||||||
|
format: 'uuid',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
requestBody: new OA\RequestBody(
|
requestBody: new OA\RequestBody(
|
||||||
required: true,
|
required: true,
|
||||||
description: 'Project updated.',
|
description: 'Project updated.',
|
||||||
|
@@ -527,6 +527,18 @@ class ServicesController extends Controller
|
|||||||
['bearerAuth' => []],
|
['bearerAuth' => []],
|
||||||
],
|
],
|
||||||
tags: ['Services'],
|
tags: ['Services'],
|
||||||
|
parameters: [
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'uuid',
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the service.',
|
||||||
|
required: true,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'string',
|
||||||
|
format: 'uuid',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
requestBody: new OA\RequestBody(
|
requestBody: new OA\RequestBody(
|
||||||
description: 'Service updated.',
|
description: 'Service updated.',
|
||||||
required: true,
|
required: true,
|
||||||
|
@@ -100,18 +100,26 @@ class Bitbucket extends Controller
|
|||||||
if ($x_bitbucket_event === 'repo:push') {
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
commit: $commit,
|
commit: $commit,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_webhook: true
|
is_webhook: true
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'application' => $application->name,
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'application' => $application->name,
|
||||||
'message' => 'Preview deployment queued.',
|
'status' => 'skipped',
|
||||||
]);
|
'message' => $result['message'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
@@ -143,7 +151,7 @@ class Bitbucket extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
@@ -152,11 +160,19 @@ class Bitbucket extends Controller
|
|||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'bitbucket'
|
git_type: 'bitbucket'
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'application' => $application->name,
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'application' => $application->name,
|
||||||
'message' => 'Preview deployment queued.',
|
'status' => 'skipped',
|
||||||
]);
|
'message' => $result['message'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
@@ -116,19 +116,27 @@ class Gitea extends Controller
|
|||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
commit: data_get($payload, 'after', 'HEAD'),
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'status' => 'success',
|
$return_payloads->push([
|
||||||
'message' => 'Deployment queued.',
|
'application' => $application->name,
|
||||||
'application_uuid' => $application->uuid,
|
'status' => 'skipped',
|
||||||
'application_name' => $application->name,
|
'message' => $result['message'],
|
||||||
]);
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$paths = str($application->watch_paths)->explode("\n");
|
$paths = str($application->watch_paths)->explode("\n");
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
@@ -175,7 +183,7 @@ class Gitea extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
@@ -184,11 +192,19 @@ class Gitea extends Controller
|
|||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'gitea'
|
git_type: 'gitea'
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'application' => $application->name,
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'application' => $application->name,
|
||||||
'message' => 'Preview deployment queued.',
|
'status' => 'skipped',
|
||||||
]);
|
'message' => $result['message'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
@@ -122,19 +122,29 @@ class Github extends Controller
|
|||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
commit: data_get($payload, 'after', 'HEAD'),
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'status' => 'success',
|
$return_payloads->push([
|
||||||
'message' => 'Deployment queued.',
|
'application' => $application->name,
|
||||||
'application_uuid' => $application->uuid,
|
'status' => 'skipped',
|
||||||
'application_name' => $application->name,
|
'message' => $result['message'],
|
||||||
]);
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
|
'deployment_uuid' => $result['deployment_uuid'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$paths = str($application->watch_paths)->explode("\n");
|
$paths = str($application->watch_paths)->explode("\n");
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
@@ -181,7 +191,8 @@ class Github extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
|
||||||
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
@@ -190,11 +201,19 @@ class Github extends Controller
|
|||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'github'
|
git_type: 'github'
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'application' => $application->name,
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'application' => $application->name,
|
||||||
'message' => 'Preview deployment queued.',
|
'status' => 'skipped',
|
||||||
]);
|
'message' => $result['message'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
@@ -341,7 +360,7 @@ class Github extends Controller
|
|||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
commit: data_get($payload, 'after', 'HEAD'),
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
@@ -349,10 +368,11 @@ class Github extends Controller
|
|||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'status' => $result['status'],
|
||||||
'message' => 'Deployment queued.',
|
'message' => $result['message'],
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'application_name' => $application->name,
|
'application_name' => $application->name,
|
||||||
|
'deployment_uuid' => $result['deployment_uuid'],
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$paths = str($application->watch_paths)->explode("\n");
|
$paths = str($application->watch_paths)->explode("\n");
|
||||||
@@ -389,7 +409,7 @@ class Github extends Controller
|
|||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
@@ -398,11 +418,19 @@ class Github extends Controller
|
|||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'github'
|
git_type: 'github'
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'application' => $application->name,
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'application' => $application->name,
|
||||||
'message' => 'Preview deployment queued.',
|
'status' => 'skipped',
|
||||||
]);
|
'message' => $result['message'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
@@ -142,19 +142,28 @@ class Gitlab extends Controller
|
|||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
commit: data_get($payload, 'after', 'HEAD'),
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'status' => 'success',
|
$return_payloads->push([
|
||||||
'message' => 'Deployment queued.',
|
'status' => $result['status'],
|
||||||
'application_uuid' => $application->uuid,
|
'message' => $result['message'],
|
||||||
'application_name' => $application->name,
|
'application_uuid' => $application->uuid,
|
||||||
]);
|
'application_name' => $application->name,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$paths = str($application->watch_paths)->explode("\n");
|
$paths = str($application->watch_paths)->explode("\n");
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
@@ -201,7 +210,7 @@ class Gitlab extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
@@ -210,11 +219,19 @@ class Gitlab extends Controller
|
|||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'gitlab'
|
git_type: 'gitlab'
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
if ($result['status'] === 'skipped') {
|
||||||
'application' => $application->name,
|
$return_payloads->push([
|
||||||
'status' => 'success',
|
'application' => $application->name,
|
||||||
'message' => 'Preview Deployment queued',
|
'status' => 'skipped',
|
||||||
]);
|
'message' => $result['message'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview Deployment queued',
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
@@ -899,100 +899,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id');
|
$sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id');
|
||||||
}
|
}
|
||||||
$ports = $this->application->main_port();
|
$ports = $this->application->main_port();
|
||||||
if ($this->pull_request_id !== 0) {
|
$coolify_envs = $this->generate_coolify_env_variables();
|
||||||
$this->env_filename = ".env-pr-$this->pull_request_id";
|
$coolify_envs->each(function ($item, $key) use ($envs) {
|
||||||
// Add SOURCE_COMMIT if not exists
|
$envs->push($key.'='.$item);
|
||||||
if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
});
|
||||||
if (! is_null($this->commit)) {
|
if ($this->pull_request_id === 0) {
|
||||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
|
||||||
} else {
|
|
||||||
$envs->push('SOURCE_COMMIT=unknown');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
|
||||||
$envs->push("COOLIFY_DOMAIN_URL={$this->preview->fqdn}");
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
|
||||||
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
|
||||||
$envs->push("COOLIFY_URL={$url}");
|
|
||||||
$envs->push("COOLIFY_DOMAIN_FQDN={$url}");
|
|
||||||
}
|
|
||||||
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_BRANCH=\"{$local_branch}\"");
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_RESOURCE_UUID={$this->application->uuid}");
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables_preview);
|
|
||||||
|
|
||||||
foreach ($sorted_environment_variables_preview as $env) {
|
|
||||||
$real_value = $env->real_value;
|
|
||||||
if ($env->version === '4.0.0-beta.239') {
|
|
||||||
$real_value = $env->real_value;
|
|
||||||
} else {
|
|
||||||
if ($env->is_literal || $env->is_multiline) {
|
|
||||||
$real_value = '\''.$real_value.'\'';
|
|
||||||
} else {
|
|
||||||
$real_value = escapeEnvVariables($env->real_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$envs->push($env->key.'='.$real_value);
|
|
||||||
}
|
|
||||||
// Add PORT if not exists, use the first port as default
|
|
||||||
if ($this->build_pack !== 'dockercompose') {
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
|
|
||||||
$envs->push("PORT={$ports[0]}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add HOST if not exists
|
|
||||||
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
|
|
||||||
$envs->push('HOST=0.0.0.0');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->env_filename = '.env';
|
$this->env_filename = '.env';
|
||||||
// Add SOURCE_COMMIT if not exists
|
|
||||||
if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
|
||||||
if (! is_null($this->commit)) {
|
|
||||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
|
||||||
} else {
|
|
||||||
$envs->push('SOURCE_COMMIT=unknown');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
|
||||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
|
||||||
$envs->push("COOLIFY_URL={$this->application->fqdn}");
|
|
||||||
} else {
|
|
||||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
|
||||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
|
||||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
|
||||||
$envs->push("COOLIFY_FQDN={$url}");
|
|
||||||
} else {
|
|
||||||
$envs->push("COOLIFY_URL={$url}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_BRANCH=\"{$local_branch}\"");
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_RESOURCE_UUID={$this->application->uuid}");
|
|
||||||
}
|
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
|
||||||
$envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables);
|
|
||||||
|
|
||||||
foreach ($sorted_environment_variables as $env) {
|
foreach ($sorted_environment_variables as $env) {
|
||||||
$real_value = $env->real_value;
|
$real_value = $env->real_value;
|
||||||
@@ -1017,6 +929,32 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
|
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
|
||||||
$envs->push('HOST=0.0.0.0');
|
$envs->push('HOST=0.0.0.0');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$this->env_filename = ".env-pr-$this->pull_request_id";
|
||||||
|
foreach ($sorted_environment_variables_preview as $env) {
|
||||||
|
$real_value = $env->real_value;
|
||||||
|
if ($env->version === '4.0.0-beta.239') {
|
||||||
|
$real_value = $env->real_value;
|
||||||
|
} else {
|
||||||
|
if ($env->is_literal || $env->is_multiline) {
|
||||||
|
$real_value = '\''.$real_value.'\'';
|
||||||
|
} else {
|
||||||
|
$real_value = escapeEnvVariables($env->real_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$envs->push($env->key.'='.$real_value);
|
||||||
|
}
|
||||||
|
// Add PORT if not exists, use the first port as default
|
||||||
|
if ($this->build_pack !== 'dockercompose') {
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
|
||||||
|
$envs->push("PORT={$ports[0]}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add HOST if not exists
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
|
||||||
|
$envs->push('HOST=0.0.0.0');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if ($envs->isEmpty()) {
|
if ($envs->isEmpty()) {
|
||||||
$this->env_filename = null;
|
$this->env_filename = null;
|
||||||
@@ -1393,6 +1331,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
foreach ($destination_ids as $destination_id) {
|
foreach ($destination_ids as $destination_id) {
|
||||||
$destination = StandaloneDocker::find($destination_id);
|
$destination = StandaloneDocker::find($destination_id);
|
||||||
|
if (! $destination) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$server = $destination->server;
|
$server = $destination->server;
|
||||||
if ($server->team_id !== $this->mainServer->team_id) {
|
if ($server->team_id !== $this->mainServer->team_id) {
|
||||||
$this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!");
|
$this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!");
|
||||||
@@ -1625,20 +1566,128 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->env_nixpacks_args = $this->env_nixpacks_args->implode(' ');
|
$this->env_nixpacks_args = $this->env_nixpacks_args->implode(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function generate_coolify_env_variables(): Collection
|
||||||
|
{
|
||||||
|
$coolify_envs = collect([]);
|
||||||
|
$local_branch = $this->branch;
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
// Add SOURCE_COMMIT if not exists
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||||
|
if (! is_null($this->commit)) {
|
||||||
|
$coolify_envs->put('SOURCE_COMMIT', $this->commit);
|
||||||
|
} else {
|
||||||
|
$coolify_envs->put('SOURCE_COMMIT', 'unknown');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_FQDN', $this->preview->fqdn);
|
||||||
|
$coolify_envs->put('COOLIFY_DOMAIN_URL', $this->preview->fqdn);
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||||
|
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||||
|
$coolify_envs->put('COOLIFY_URL', $url);
|
||||||
|
$coolify_envs->put('COOLIFY_DOMAIN_FQDN', $url);
|
||||||
|
}
|
||||||
|
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_BRANCH', $local_branch);
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_RESOURCE_UUID', $this->application->uuid);
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_CONTAINER_NAME', $this->container_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_coolify_default_environment_variables($this->application, $coolify_envs, $this->application->environment_variables_preview);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Add SOURCE_COMMIT if not exists
|
||||||
|
if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||||
|
if (! is_null($this->commit)) {
|
||||||
|
$coolify_envs->put('SOURCE_COMMIT', $this->commit);
|
||||||
|
} else {
|
||||||
|
$coolify_envs->put('SOURCE_COMMIT', 'unknown');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||||
|
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||||
|
$coolify_envs->put('COOLIFY_URL', $this->application->fqdn);
|
||||||
|
} else {
|
||||||
|
$coolify_envs->put('COOLIFY_FQDN', $this->application->fqdn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||||
|
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||||
|
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||||
|
$coolify_envs->put('COOLIFY_FQDN', $url);
|
||||||
|
} else {
|
||||||
|
$coolify_envs->put('COOLIFY_URL', $url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||||
|
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_BRANCH', $local_branch);
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_RESOURCE_UUID', $this->application->uuid);
|
||||||
|
}
|
||||||
|
if ($this->application->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||||
|
$coolify_envs->put('COOLIFY_CONTAINER_NAME', $this->container_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_coolify_default_environment_variables($this->application, $coolify_envs, $this->application->environment_variables);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $coolify_envs;
|
||||||
|
}
|
||||||
|
|
||||||
private function generate_env_variables()
|
private function generate_env_variables()
|
||||||
{
|
{
|
||||||
$this->env_args = collect([]);
|
$this->env_args = collect([]);
|
||||||
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
||||||
|
$coolify_envs = $this->generate_coolify_env_variables();
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
foreach ($this->application->build_environment_variables as $env) {
|
foreach ($this->application->build_environment_variables as $env) {
|
||||||
if (! is_null($env->real_value)) {
|
if (! is_null($env->real_value)) {
|
||||||
$this->env_args->put($env->key, $env->real_value);
|
$this->env_args->put($env->key, $env->real_value);
|
||||||
|
if (str($env->real_value)->startsWith('$')) {
|
||||||
|
$variable_key = str($env->real_value)->after('$');
|
||||||
|
if ($variable_key->startsWith('COOLIFY_')) {
|
||||||
|
$variable = $coolify_envs->get($variable_key->value());
|
||||||
|
if (filled($variable)) {
|
||||||
|
$this->env_args->prepend($variable, $variable_key->value());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$variable = $this->application->environment_variables()->where('key', $variable_key)->first();
|
||||||
|
if ($variable) {
|
||||||
|
$this->env_args->prepend($variable->real_value, $env->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foreach ($this->application->build_environment_variables_preview as $env) {
|
foreach ($this->application->build_environment_variables_preview as $env) {
|
||||||
if (! is_null($env->real_value)) {
|
if (! is_null($env->real_value)) {
|
||||||
$this->env_args->put($env->key, $env->real_value);
|
$this->env_args->put($env->key, $env->real_value);
|
||||||
|
if (str($env->real_value)->startsWith('$')) {
|
||||||
|
$variable_key = str($env->real_value)->after('$');
|
||||||
|
if ($variable_key->startsWith('COOLIFY_')) {
|
||||||
|
$variable = $coolify_envs->get($variable_key->value());
|
||||||
|
if (filled($variable)) {
|
||||||
|
$this->env_args->prepend($variable, $variable_key->value());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$variable = $this->application->environment_variables_preview()->where('key', $variable_key)->first();
|
||||||
|
if ($variable) {
|
||||||
|
$this->env_args->prepend($variable->real_value, $env->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2413,20 +2462,23 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
private function next(string $status)
|
private function next(string $status)
|
||||||
{
|
{
|
||||||
queue_next_deployment($this->application);
|
queue_next_deployment($this->application);
|
||||||
// If the deployment is cancelled by the user, don't update the status
|
|
||||||
if (
|
// Never allow changing status from FAILED or CANCELLED_BY_USER to anything else
|
||||||
$this->application_deployment_queue->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value &&
|
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value ||
|
||||||
$this->application_deployment_queue->status !== ApplicationDeploymentStatus::FAILED->value
|
$this->application_deployment_queue->status === ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||||
) {
|
return;
|
||||||
$this->application_deployment_queue->update([
|
|
||||||
'status' => $status,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) {
|
|
||||||
|
$this->application_deployment_queue->update([
|
||||||
|
'status' => $status,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($status === ApplicationDeploymentStatus::FAILED->value) {
|
||||||
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
||||||
if (! $this->only_this_server) {
|
if (! $this->only_this_server) {
|
||||||
$this->deploy_to_additional_destinations();
|
$this->deploy_to_additional_destinations();
|
||||||
|
@@ -269,7 +269,7 @@ class Email extends Component
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->smtpEnabled = false;
|
$this->smtpEnabled = false;
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,32 +337,29 @@ class Email extends Component
|
|||||||
public function copyFromInstanceSettings()
|
public function copyFromInstanceSettings()
|
||||||
{
|
{
|
||||||
$settings = instanceSettings();
|
$settings = instanceSettings();
|
||||||
|
$this->smtpFromAddress = $settings->smtp_from_address;
|
||||||
|
$this->smtpFromName = $settings->smtp_from_name;
|
||||||
|
|
||||||
if ($settings->smtp_enabled) {
|
if ($settings->smtp_enabled) {
|
||||||
$this->smtpEnabled = true;
|
$this->smtpEnabled = true;
|
||||||
$this->smtpFromAddress = $settings->smtp_from_address;
|
|
||||||
$this->smtpFromName = $settings->smtp_from_name;
|
|
||||||
$this->smtpRecipients = $settings->smtp_recipients;
|
|
||||||
$this->smtpHost = $settings->smtp_host;
|
|
||||||
$this->smtpPort = $settings->smtp_port;
|
|
||||||
$this->smtpEncryption = $settings->smtp_encryption;
|
|
||||||
$this->smtpUsername = $settings->smtp_username;
|
|
||||||
$this->smtpPassword = $settings->smtp_password;
|
|
||||||
$this->smtpTimeout = $settings->smtp_timeout;
|
|
||||||
$this->resendEnabled = false;
|
$this->resendEnabled = false;
|
||||||
$this->saveModel();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->smtpRecipients = $settings->smtp_recipients;
|
||||||
|
$this->smtpHost = $settings->smtp_host;
|
||||||
|
$this->smtpPort = $settings->smtp_port;
|
||||||
|
$this->smtpEncryption = $settings->smtp_encryption;
|
||||||
|
$this->smtpUsername = $settings->smtp_username;
|
||||||
|
$this->smtpPassword = $settings->smtp_password;
|
||||||
|
$this->smtpTimeout = $settings->smtp_timeout;
|
||||||
|
|
||||||
if ($settings->resend_enabled) {
|
if ($settings->resend_enabled) {
|
||||||
$this->resendEnabled = true;
|
$this->resendEnabled = true;
|
||||||
$this->resendApiKey = $settings->resend_api_key;
|
|
||||||
$this->smtpEnabled = false;
|
$this->smtpEnabled = false;
|
||||||
$this->saveModel();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
$this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.');
|
$this->resendApiKey = $settings->resend_api_key;
|
||||||
|
$this->saveModel();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@@ -84,11 +84,16 @@ class Heading extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $this->application,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
force_rebuild: $force_rebuild,
|
force_rebuild: $force_rebuild,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
$this->dispatch('success', 'Deployment skipped', $result['message']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.deployment.show', [
|
return $this->redirectRoute('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
@@ -126,11 +131,16 @@ class Heading extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $this->application,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
restart_only: true,
|
restart_only: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
$this->dispatch('success', 'Deployment skipped', $result['message']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.deployment.show', [
|
return $this->redirectRoute('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
@@ -159,13 +159,18 @@ class Previews extends Component
|
|||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
application: $this->application,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deployment_uuid,
|
deployment_uuid: $this->deployment_uuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
git_type: $found->git_type ?? null,
|
git_type: $found->git_type ?? null,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
$this->dispatch('success', 'Deployment skipped', $result['message']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('project.application.deployment.show', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
@@ -30,11 +30,15 @@ class Source extends Component
|
|||||||
#[Validate(['nullable', 'string'])]
|
#[Validate(['nullable', 'string'])]
|
||||||
public ?string $gitCommitSha = null;
|
public ?string $gitCommitSha = null;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public $sources;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->syncData();
|
$this->syncData();
|
||||||
$this->getPrivateKeys();
|
$this->getPrivateKeys();
|
||||||
|
$this->getSources();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
handleError($e, $this);
|
handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -66,6 +70,14 @@ class Source extends Component
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getSources()
|
||||||
|
{
|
||||||
|
// filter the current source out
|
||||||
|
$this->sources = currentTeam()->sources()->whereNotNull('app_id')->reject(function ($source) {
|
||||||
|
return $source->id === $this->application->source_id;
|
||||||
|
})->sortBy('name');
|
||||||
|
}
|
||||||
|
|
||||||
public function setPrivateKey(int $privateKeyId)
|
public function setPrivateKey(int $privateKeyId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -92,4 +104,19 @@ class Source extends Component
|
|||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function changeSource($sourceId, $sourceType)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->application->update([
|
||||||
|
'source_id' => $sourceId,
|
||||||
|
'source_type' => $sourceType,
|
||||||
|
]);
|
||||||
|
$this->application->refresh();
|
||||||
|
$this->getSources();
|
||||||
|
$this->dispatch('success', 'Source updated!');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,7 +79,7 @@ class Destination extends Component
|
|||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
$server = Server::ownedByCurrentTeam()->findOrFail($server_id);
|
$server = Server::ownedByCurrentTeam()->findOrFail($server_id);
|
||||||
$destination = $server->standaloneDockers->where('id', $network_id)->firstOrFail();
|
$destination = $server->standaloneDockers->where('id', $network_id)->firstOrFail();
|
||||||
queue_application_deployment(
|
$result = queue_application_deployment(
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
application: $this->resource,
|
application: $this->resource,
|
||||||
server: $server,
|
server: $server,
|
||||||
@@ -87,6 +87,11 @@ class Destination extends Component
|
|||||||
only_this_server: true,
|
only_this_server: true,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
);
|
);
|
||||||
|
if ($result['status'] === 'skipped') {
|
||||||
|
$this->dispatch('success', 'Deployment skipped', $result['message']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('project.application.deployment.show', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => data_get($this->resource, 'environment.project.uuid'),
|
'project_uuid' => data_get($this->resource, 'environment.project.uuid'),
|
||||||
|
@@ -38,7 +38,8 @@ class DynamicConfigurations extends Component
|
|||||||
$contents = collect([]);
|
$contents = collect([]);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
$without_extension = str_replace('.', '|', $file);
|
$without_extension = str_replace('.', '|', $file);
|
||||||
$contents[$without_extension] = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server);
|
$content = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server);
|
||||||
|
$contents[$without_extension] = $content ?? '';
|
||||||
}
|
}
|
||||||
$this->contents = $contents;
|
$this->contents = $contents;
|
||||||
$this->dispatch('$refresh');
|
$this->dispatch('$refresh');
|
||||||
|
@@ -177,7 +177,7 @@ class SettingsEmail extends Component
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->smtpEnabled = false;
|
$this->smtpEnabled = false;
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ class SettingsEmail extends Component
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->resendEnabled = false;
|
$this->resendEnabled = false;
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,19 +12,30 @@ class Index extends Component
|
|||||||
|
|
||||||
public bool $alreadySubscribed = false;
|
public bool $alreadySubscribed = false;
|
||||||
|
|
||||||
|
public bool $isUnpaid = false;
|
||||||
|
|
||||||
|
public bool $isCancelled = false;
|
||||||
|
|
||||||
|
public bool $isMember = false;
|
||||||
|
|
||||||
|
public bool $loading = true;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (! isCloud()) {
|
if (! isCloud()) {
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
if (auth()->user()?->isMember()) {
|
if (auth()->user()?->isMember()) {
|
||||||
return redirect()->route('dashboard');
|
$this->isMember = true;
|
||||||
}
|
}
|
||||||
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||||
return redirect()->route('subscription.show');
|
return redirect()->route('subscription.show');
|
||||||
}
|
}
|
||||||
$this->settings = instanceSettings();
|
$this->settings = instanceSettings();
|
||||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||||
|
if (! $this->alreadySubscribed) {
|
||||||
|
$this->loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stripeCustomerPortal()
|
public function stripeCustomerPortal()
|
||||||
@@ -37,6 +48,41 @@ class Index extends Component
|
|||||||
return redirect($session->url);
|
return redirect($session->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getStripeStatus()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$subscription = currentTeam()->subscription;
|
||||||
|
$stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
|
||||||
|
$customer = $stripe->customers->retrieve(currentTeam()->subscription->stripe_customer_id);
|
||||||
|
if ($customer) {
|
||||||
|
$subscriptions = $stripe->subscriptions->all(['customer' => $customer->id]);
|
||||||
|
$currentTeam = currentTeam()->id ?? null;
|
||||||
|
if (count($subscriptions->data) > 0 && $currentTeam) {
|
||||||
|
$foundSubscription = collect($subscriptions->data)->firstWhere('metadata.team_id', $currentTeam);
|
||||||
|
if ($foundSubscription) {
|
||||||
|
$status = data_get($foundSubscription, 'status');
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_subscription_id' => $foundSubscription->id,
|
||||||
|
]);
|
||||||
|
if ($status === 'unpaid') {
|
||||||
|
$this->isUnpaid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count($subscriptions->data) === 0) {
|
||||||
|
$this->isCancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Log the error
|
||||||
|
logger()->error('Stripe API error: ' . $e->getMessage());
|
||||||
|
// Set a flag to show an error message to the user
|
||||||
|
$this->addError('stripe', 'Could not retrieve subscription information. Please try again later.');
|
||||||
|
} finally {
|
||||||
|
$this->loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.subscription.index');
|
return view('livewire.subscription.index');
|
||||||
|
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
||||||
#[OA\Schema(
|
#[OA\Schema(
|
||||||
@@ -101,17 +102,23 @@ class ApplicationDeploymentQueue extends Model
|
|||||||
'hidden' => $hidden,
|
'hidden' => $hidden,
|
||||||
'batch' => 1,
|
'batch' => 1,
|
||||||
];
|
];
|
||||||
if ($this->logs) {
|
|
||||||
$previousLogs = json_decode($this->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
// Use a transaction to ensure atomicity
|
||||||
$newLogEntry['order'] = count($previousLogs) + 1;
|
DB::transaction(function () use ($newLogEntry) {
|
||||||
$previousLogs[] = $newLogEntry;
|
// Reload the model to get the latest logs
|
||||||
$this->update([
|
$this->refresh();
|
||||||
'logs' => json_encode($previousLogs, flags: JSON_THROW_ON_ERROR),
|
|
||||||
]);
|
if ($this->logs) {
|
||||||
} else {
|
$previousLogs = json_decode($this->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||||
$this->update([
|
$newLogEntry['order'] = count($previousLogs) + 1;
|
||||||
'logs' => json_encode([$newLogEntry], flags: JSON_THROW_ON_ERROR),
|
$previousLogs[] = $newLogEntry;
|
||||||
]);
|
$this->logs = json_encode($previousLogs, flags: JSON_THROW_ON_ERROR);
|
||||||
}
|
} else {
|
||||||
|
$this->logs = json_encode([$newLogEntry], flags: JSON_THROW_ON_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save without triggering events to prevent potential race conditions
|
||||||
|
$this->saveQuietly();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,8 @@ use phpseclib3\Crypt\PublicKeyLoader;
|
|||||||
'name' => ['type' => 'string'],
|
'name' => ['type' => 'string'],
|
||||||
'description' => ['type' => 'string'],
|
'description' => ['type' => 'string'],
|
||||||
'private_key' => ['type' => 'string', 'format' => 'private-key'],
|
'private_key' => ['type' => 'string', 'format' => 'private-key'],
|
||||||
|
'public_key' => ['type' => 'string'],
|
||||||
|
'fingerprint' => ['type' => 'string'],
|
||||||
'is_git_related' => ['type' => 'boolean'],
|
'is_git_related' => ['type' => 'boolean'],
|
||||||
'team_id' => ['type' => 'integer'],
|
'team_id' => ['type' => 'integer'],
|
||||||
'created_at' => ['type' => 'string'],
|
'created_at' => ['type' => 'string'],
|
||||||
|
@@ -20,7 +20,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Stringable;
|
use Illuminate\Support\Stringable;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
@@ -1026,22 +1025,11 @@ $schema://$host {
|
|||||||
$this->refresh();
|
$this->refresh();
|
||||||
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
||||||
$isReachable = (bool) $this->settings->is_reachable;
|
$isReachable = (bool) $this->settings->is_reachable;
|
||||||
|
|
||||||
Log::debug('Server reachability check', [
|
|
||||||
'server_id' => $this->id,
|
|
||||||
'is_reachable' => $isReachable,
|
|
||||||
'notification_sent' => $unreachableNotificationSent,
|
|
||||||
'unreachable_count' => $this->unreachable_count,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($isReachable === true) {
|
if ($isReachable === true) {
|
||||||
$this->unreachable_count = 0;
|
$this->unreachable_count = 0;
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
if ($unreachableNotificationSent === true) {
|
if ($unreachableNotificationSent === true) {
|
||||||
Log::debug('Server is now reachable, sending notification', [
|
|
||||||
'server_id' => $this->id,
|
|
||||||
]);
|
|
||||||
$this->sendReachableNotification();
|
$this->sendReachableNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,17 +1037,10 @@ $schema://$host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->increment('unreachable_count');
|
$this->increment('unreachable_count');
|
||||||
Log::debug('Incremented unreachable count', [
|
|
||||||
'server_id' => $this->id,
|
|
||||||
'new_count' => $this->unreachable_count,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($this->unreachable_count === 1) {
|
if ($this->unreachable_count === 1) {
|
||||||
$this->settings->is_reachable = true;
|
$this->settings->is_reachable = true;
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
Log::debug('First unreachable attempt, marking as reachable', [
|
|
||||||
'server_id' => $this->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1068,11 +1049,6 @@ $schema://$host {
|
|||||||
$failedChecks = 0;
|
$failedChecks = 0;
|
||||||
for ($i = 0; $i < 3; $i++) {
|
for ($i = 0; $i < 3; $i++) {
|
||||||
$status = $this->serverStatus();
|
$status = $this->serverStatus();
|
||||||
Log::debug('Additional reachability check', [
|
|
||||||
'server_id' => $this->id,
|
|
||||||
'attempt' => $i + 1,
|
|
||||||
'status' => $status,
|
|
||||||
]);
|
|
||||||
sleep(5);
|
sleep(5);
|
||||||
if (! $status) {
|
if (! $status) {
|
||||||
$failedChecks++;
|
$failedChecks++;
|
||||||
@@ -1080,9 +1056,6 @@ $schema://$host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($failedChecks === 3 && ! $unreachableNotificationSent) {
|
if ($failedChecks === 3 && ! $unreachableNotificationSent) {
|
||||||
Log::debug('Server confirmed unreachable after 3 attempts, sending notification', [
|
|
||||||
'server_id' => $this->id,
|
|
||||||
]);
|
|
||||||
$this->sendUnreachableNotification();
|
$this->sendUnreachableNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -192,8 +192,6 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
|||||||
public function subscriptionEnded()
|
public function subscriptionEnded()
|
||||||
{
|
{
|
||||||
$this->subscription->update([
|
$this->subscription->update([
|
||||||
'stripe_subscription_id' => null,
|
|
||||||
'stripe_plan_id' => null,
|
|
||||||
'stripe_cancel_at_period_end' => false,
|
'stripe_cancel_at_period_end' => false,
|
||||||
'stripe_invoice_paid' => false,
|
'stripe_invoice_paid' => false,
|
||||||
'stripe_trial_already_ended' => false,
|
'stripe_trial_already_ended' => false,
|
||||||
|
@@ -24,6 +24,26 @@ function queue_application_deployment(Application $application, string $deployme
|
|||||||
if ($destination) {
|
if ($destination) {
|
||||||
$destination_id = $destination->id;
|
$destination_id = $destination->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if there's already a deployment in progress or queued for this application and commit
|
||||||
|
$existing_deployment = ApplicationDeploymentQueue::where('application_id', $application_id)
|
||||||
|
->where('commit', $commit)
|
||||||
|
->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($existing_deployment) {
|
||||||
|
// If force_rebuild is true or rollback is true or no_questions_asked is true, we'll still create a new deployment
|
||||||
|
if (! $force_rebuild && ! $rollback && ! $no_questions_asked) {
|
||||||
|
// Return the existing deployment's details
|
||||||
|
return [
|
||||||
|
'status' => 'skipped',
|
||||||
|
'message' => 'Deployment already queued for this commit.',
|
||||||
|
'deployment_uuid' => $existing_deployment->deployment_uuid,
|
||||||
|
'existing_deployment' => $existing_deployment,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$deployment = ApplicationDeploymentQueue::create([
|
$deployment = ApplicationDeploymentQueue::create([
|
||||||
'application_id' => $application_id,
|
'application_id' => $application_id,
|
||||||
'application_name' => $application->name,
|
'application_name' => $application->name,
|
||||||
@@ -47,11 +67,17 @@ function queue_application_deployment(Application $application, string $deployme
|
|||||||
ApplicationDeploymentJob::dispatch(
|
ApplicationDeploymentJob::dispatch(
|
||||||
application_deployment_queue_id: $deployment->id,
|
application_deployment_queue_id: $deployment->id,
|
||||||
);
|
);
|
||||||
} elseif (next_queuable($server_id, $application_id)) {
|
} elseif (next_queuable($server_id, $application_id, $commit)) {
|
||||||
ApplicationDeploymentJob::dispatch(
|
ApplicationDeploymentJob::dispatch(
|
||||||
application_deployment_queue_id: $deployment->id,
|
application_deployment_queue_id: $deployment->id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => 'queued',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
'deployment_uuid' => $deployment_uuid,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
function force_start_deployment(ApplicationDeploymentQueue $deployment)
|
function force_start_deployment(ApplicationDeploymentQueue $deployment)
|
||||||
{
|
{
|
||||||
@@ -78,20 +104,35 @@ function queue_next_deployment(Application $application)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function next_queuable(string $server_id, string $application_id): bool
|
function next_queuable(string $server_id, string $application_id, string $commit = 'HEAD'): bool
|
||||||
{
|
{
|
||||||
$deployments = ApplicationDeploymentQueue::where('server_id', $server_id)->whereIn('status', ['in_progress', ApplicationDeploymentStatus::QUEUED])->get()->sortByDesc('created_at');
|
// Check if there's already a deployment in progress for this application and commit
|
||||||
$same_application_deployments = $deployments->where('application_id', $application_id);
|
$existing_deployment = ApplicationDeploymentQueue::where('application_id', $application_id)
|
||||||
$in_progress = $same_application_deployments->filter(function ($value, $key) {
|
->where('commit', $commit)
|
||||||
return $value->status === 'in_progress';
|
->where('status', ApplicationDeploymentStatus::IN_PROGRESS->value)
|
||||||
});
|
->first();
|
||||||
if ($in_progress->count() > 0) {
|
|
||||||
|
if ($existing_deployment) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if there's any deployment in progress for this application
|
||||||
|
$in_progress = ApplicationDeploymentQueue::where('application_id', $application_id)
|
||||||
|
->where('status', ApplicationDeploymentStatus::IN_PROGRESS->value)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($in_progress) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check server's concurrent build limit
|
||||||
$server = Server::find($server_id);
|
$server = Server::find($server_id);
|
||||||
$concurrent_builds = $server->settings->concurrent_builds;
|
$concurrent_builds = $server->settings->concurrent_builds;
|
||||||
|
$active_deployments = ApplicationDeploymentQueue::where('server_id', $server_id)
|
||||||
|
->where('status', ApplicationDeploymentStatus::IN_PROGRESS->value)
|
||||||
|
->count();
|
||||||
|
|
||||||
if ($deployments->count() > $concurrent_builds) {
|
if ($active_deployments >= $concurrent_builds) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -52,6 +52,9 @@ function generateGithubToken(GithubApp $source, string $type)
|
|||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
$error = data_get($response->json(), 'message', 'no error message found');
|
$error = data_get($response->json(), 'message', 'no error message found');
|
||||||
|
if ($error === 'Not Found') {
|
||||||
|
$error = 'Repository not found. Is it moved or deleted?';
|
||||||
|
}
|
||||||
throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".$error);
|
throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".$error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2987,7 +2987,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$predefinedPort = '8000';
|
$predefinedPort = '8000';
|
||||||
}
|
}
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
|
$applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first();
|
||||||
if ($applicationFound) {
|
if ($applicationFound) {
|
||||||
$savedService = $applicationFound;
|
$savedService = $applicationFound;
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
@@ -2999,17 +2999,22 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
} else {
|
} else {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
'image' => $image,
|
|
||||||
'service_id' => $resource->id,
|
'service_id' => $resource->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$savedService = ServiceApplication::firstOrCreate([
|
$savedService = ServiceApplication::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
'image' => $image,
|
|
||||||
'service_id' => $resource->id,
|
'service_id' => $resource->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if image changed
|
||||||
|
if ($savedService->image !== $image) {
|
||||||
|
$savedService->image = $image;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
|
|
||||||
$environment = collect(data_get($service, 'environment', []));
|
$environment = collect(data_get($service, 'environment', []));
|
||||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||||
$environment = $environment->merge($buildArgs);
|
$environment = $environment->merge($buildArgs);
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'coolify' => [
|
'coolify' => [
|
||||||
'version' => '4.0.0-beta.407',
|
'version' => '4.0.0-beta.408',
|
||||||
'helper_version' => '1.0.8',
|
'helper_version' => '1.0.8',
|
||||||
'realtime_version' => '1.0.6',
|
'realtime_version' => '1.0.6',
|
||||||
'self_hosted' => env('SELF_HOSTED', true),
|
'self_hosted' => env('SELF_HOSTED', true),
|
||||||
@@ -10,6 +10,7 @@ return [
|
|||||||
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
|
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
|
||||||
'registry_url' => env('REGISTRY_URL', 'ghcr.io'),
|
'registry_url' => env('REGISTRY_URL', 'ghcr.io'),
|
||||||
'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'),
|
'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'),
|
||||||
|
'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-realtime'),
|
||||||
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),
|
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@@ -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('subscriptions', function (Blueprint $table) {
|
||||||
|
$table->longText('stripe_comment')->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('subscriptions', function (Blueprint $table) {
|
||||||
|
$table->longText('stripe_comment')->nullable(false)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -5,9 +5,9 @@
|
|||||||
"@xterm/addon-fit": "0.10.0",
|
"@xterm/addon-fit": "0.10.0",
|
||||||
"@xterm/xterm": "5.5.0",
|
"@xterm/xterm": "5.5.0",
|
||||||
"cookie": "1.0.2",
|
"cookie": "1.0.2",
|
||||||
"axios": "1.7.9",
|
"axios": "1.8.4",
|
||||||
"dotenv": "16.4.7",
|
"dotenv": "16.5.0",
|
||||||
"node-pty": "1.0.0",
|
"node-pty": "1.0.0",
|
||||||
"ws": "8.18.1"
|
"ws": "8.18.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -89,9 +89,9 @@ RUN echo "alias ll='ls -al'" >> /etc/profile && \
|
|||||||
# Install Cloudflared based on architecture
|
# Install Cloudflared based on architecture
|
||||||
RUN mkdir -p /usr/local/bin && \
|
RUN mkdir -p /usr/local/bin && \
|
||||||
if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \
|
if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \
|
||||||
curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64" -o /usr/local/bin/cloudflared; \
|
curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64" -o /usr/local/bin/cloudflared; \
|
||||||
elif [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
|
elif [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
|
||||||
curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64" -o /usr/local/bin/cloudflared; \
|
curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64" -o /usr/local/bin/cloudflared; \
|
||||||
fi && \
|
fi && \
|
||||||
chmod +x /usr/local/bin/cloudflared
|
chmod +x /usr/local/bin/cloudflared
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Detect whether /dev/tty is available & functional
|
# Detect whether /dev/tty is available & functional
|
||||||
if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then
|
if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then
|
||||||
exec < /dev/tty
|
exec </dev/tty
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get list of stashed PHP files
|
# Get list of stashed PHP files
|
||||||
|
93
openapi.json
93
openapi.json
@@ -1798,6 +1798,18 @@
|
|||||||
"summary": "Update",
|
"summary": "Update",
|
||||||
"description": "Update application by UUID.",
|
"description": "Update application by UUID.",
|
||||||
"operationId": "update-application-by-uuid",
|
"operationId": "update-application-by-uuid",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "uuid",
|
||||||
|
"in": "path",
|
||||||
|
"description": "UUID of the application.",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Application updated.",
|
"description": "Application updated.",
|
||||||
"required": true,
|
"required": true,
|
||||||
@@ -4441,7 +4453,7 @@
|
|||||||
"Deployments"
|
"Deployments"
|
||||||
],
|
],
|
||||||
"summary": "Deploy",
|
"summary": "Deploy",
|
||||||
"description": "Deploy by tag or uuid. `Post` request also accepted.",
|
"description": "Deploy by tag or uuid. `Post` request also accepted with `uuid` and `tag` json body.",
|
||||||
"operationId": "deploy-by-tag-or-uuid",
|
"operationId": "deploy-by-tag-or-uuid",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -4529,6 +4541,40 @@
|
|||||||
"summary": "List application deployments",
|
"summary": "List application deployments",
|
||||||
"description": "List application deployments by using the app uuid",
|
"description": "List application deployments by using the app uuid",
|
||||||
"operationId": "list-deployments-by-app-uuid",
|
"operationId": "list-deployments-by-app-uuid",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "uuid",
|
||||||
|
"in": "path",
|
||||||
|
"description": "UUID of the application.",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "skip",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Number of records to skip.",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 0,
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "take",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Number of records to take.",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"default": 10,
|
||||||
|
"minimum": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "List application deployments by using the app uuid.",
|
"description": "List application deployments by using the app uuid.",
|
||||||
@@ -4921,6 +4967,18 @@
|
|||||||
"summary": "Update",
|
"summary": "Update",
|
||||||
"description": "Update Project.",
|
"description": "Update Project.",
|
||||||
"operationId": "update-project-by-uuid",
|
"operationId": "update-project-by-uuid",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "uuid",
|
||||||
|
"in": "path",
|
||||||
|
"description": "UUID of the project.",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Project updated.",
|
"description": "Project updated.",
|
||||||
"required": true,
|
"required": true,
|
||||||
@@ -5321,6 +5379,22 @@
|
|||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"description": "Private Key not found."
|
"description": "Private Key not found."
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Private Key is in use and cannot be deleted.",
|
||||||
|
"content": {
|
||||||
|
"application\/json": {
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Private Key is in use and cannot be deleted."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"security": [
|
"security": [
|
||||||
@@ -6222,6 +6296,18 @@
|
|||||||
"summary": "Update",
|
"summary": "Update",
|
||||||
"description": "Update service by UUID.",
|
"description": "Update service by UUID.",
|
||||||
"operationId": "update-service-by-uuid",
|
"operationId": "update-service-by-uuid",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "uuid",
|
||||||
|
"in": "path",
|
||||||
|
"description": "UUID of the service.",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"description": "Service updated.",
|
"description": "Service updated.",
|
||||||
"required": true,
|
"required": true,
|
||||||
@@ -7198,6 +7284,11 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"description": "Ports mappings."
|
"description": "Ports mappings."
|
||||||
},
|
},
|
||||||
|
"custom_network_aliases": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "Network aliases for Docker container."
|
||||||
|
},
|
||||||
"base_directory": {
|
"base_directory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Base directory for all commands."
|
"description": "Base directory for all commands."
|
||||||
|
68
openapi.yaml
68
openapi.yaml
@@ -1277,6 +1277,15 @@ paths:
|
|||||||
summary: Update
|
summary: Update
|
||||||
description: 'Update application by UUID.'
|
description: 'Update application by UUID.'
|
||||||
operationId: update-application-by-uuid
|
operationId: update-application-by-uuid
|
||||||
|
parameters:
|
||||||
|
-
|
||||||
|
name: uuid
|
||||||
|
in: path
|
||||||
|
description: 'UUID of the application.'
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
requestBody:
|
requestBody:
|
||||||
description: 'Application updated.'
|
description: 'Application updated.'
|
||||||
required: true
|
required: true
|
||||||
@@ -3085,7 +3094,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Deployments
|
- Deployments
|
||||||
summary: Deploy
|
summary: Deploy
|
||||||
description: 'Deploy by tag or uuid. `Post` request also accepted.'
|
description: 'Deploy by tag or uuid. `Post` request also accepted with `uuid` and `tag` json body.'
|
||||||
operationId: deploy-by-tag-or-uuid
|
operationId: deploy-by-tag-or-uuid
|
||||||
parameters:
|
parameters:
|
||||||
-
|
-
|
||||||
@@ -3135,6 +3144,33 @@ paths:
|
|||||||
summary: 'List application deployments'
|
summary: 'List application deployments'
|
||||||
description: 'List application deployments by using the app uuid'
|
description: 'List application deployments by using the app uuid'
|
||||||
operationId: list-deployments-by-app-uuid
|
operationId: list-deployments-by-app-uuid
|
||||||
|
parameters:
|
||||||
|
-
|
||||||
|
name: uuid
|
||||||
|
in: path
|
||||||
|
description: 'UUID of the application.'
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
-
|
||||||
|
name: skip
|
||||||
|
in: query
|
||||||
|
description: 'Number of records to skip.'
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
minimum: 0
|
||||||
|
-
|
||||||
|
name: take
|
||||||
|
in: query
|
||||||
|
description: 'Number of records to take.'
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 10
|
||||||
|
minimum: 1
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: 'List application deployments by using the app uuid.'
|
description: 'List application deployments by using the app uuid.'
|
||||||
@@ -3377,6 +3413,15 @@ paths:
|
|||||||
summary: Update
|
summary: Update
|
||||||
description: 'Update Project.'
|
description: 'Update Project.'
|
||||||
operationId: update-project-by-uuid
|
operationId: update-project-by-uuid
|
||||||
|
parameters:
|
||||||
|
-
|
||||||
|
name: uuid
|
||||||
|
in: path
|
||||||
|
description: 'UUID of the project.'
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
requestBody:
|
requestBody:
|
||||||
description: 'Project updated.'
|
description: 'Project updated.'
|
||||||
required: true
|
required: true
|
||||||
@@ -3630,6 +3675,14 @@ paths:
|
|||||||
$ref: '#/components/responses/400'
|
$ref: '#/components/responses/400'
|
||||||
'404':
|
'404':
|
||||||
description: 'Private Key not found.'
|
description: 'Private Key not found.'
|
||||||
|
'422':
|
||||||
|
description: 'Private Key is in use and cannot be deleted.'
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
message: { type: string, example: 'Private Key is in use and cannot be deleted.' }
|
||||||
|
type: object
|
||||||
security:
|
security:
|
||||||
-
|
-
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
@@ -4145,6 +4198,15 @@ paths:
|
|||||||
summary: Update
|
summary: Update
|
||||||
description: 'Update service by UUID.'
|
description: 'Update service by UUID.'
|
||||||
operationId: update-service-by-uuid
|
operationId: update-service-by-uuid
|
||||||
|
parameters:
|
||||||
|
-
|
||||||
|
name: uuid
|
||||||
|
in: path
|
||||||
|
description: 'UUID of the service.'
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
requestBody:
|
requestBody:
|
||||||
description: 'Service updated.'
|
description: 'Service updated.'
|
||||||
required: true
|
required: true
|
||||||
@@ -4769,6 +4831,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
description: 'Ports mappings.'
|
description: 'Ports mappings.'
|
||||||
|
custom_network_aliases:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
description: 'Network aliases for Docker container.'
|
||||||
base_directory:
|
base_directory:
|
||||||
type: string
|
type: string
|
||||||
description: 'Base directory for all commands.'
|
description: 'Base directory for all commands.'
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.407"
|
"version": "4.0.0-beta.408"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.408"
|
"version": "4.0.0-beta.410"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.8"
|
"version": "1.0.8"
|
||||||
},
|
},
|
||||||
"realtime": {
|
"realtime": {
|
||||||
"version": "1.0.6"
|
"version": "1.0.7"
|
||||||
},
|
},
|
||||||
"sentinel": {
|
"sentinel": {
|
||||||
"version": "0.0.15"
|
"version": "0.0.15"
|
||||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@@ -22,7 +22,7 @@
|
|||||||
"pusher-js": "8.4.0",
|
"pusher-js": "8.4.0",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "3.4.17",
|
"tailwindcss": "3.4.17",
|
||||||
"vite": "^6.2.4",
|
"vite": "^6.2.6",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2994,9 +2994,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.2.4",
|
"version": "6.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
|
||||||
"integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==",
|
"integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
"pusher-js": "8.4.0",
|
"pusher-js": "8.4.0",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "3.4.17",
|
"tailwindcss": "3.4.17",
|
||||||
"vite": "^6.2.4",
|
"vite": "^6.2.6",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@@ -8,13 +8,8 @@
|
|||||||
<h1>Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
<div class="subtitle">Your self-hosted infrastructure.</div>
|
<div class="subtitle">Your self-hosted infrastructure.</div>
|
||||||
@if (request()->query->get('success'))
|
@if (request()->query->get('success'))
|
||||||
<div class="items-center justify-center mb-10 font-bold rounded alert alert-success">
|
<div class=" mb-10 font-bold alert alert-success">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
Your subscription has been activated! Welcome onboard! It could take a few seconds before your
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
Your subscription has been activated! Welcome onboard! <br>It could take a few seconds before your
|
|
||||||
subscription is activated.<br> Please be patient.
|
subscription is activated.<br> Please be patient.
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@@ -1,13 +1,6 @@
|
|||||||
<div>
|
<div class="w-full px-2">
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation buttonFullWidth title="Confirm Team Deletion?" buttonTitle="Delete Team" isErrorButton
|
||||||
title="Confirm Team Deletion?"
|
submitAction="delete" :actions="['The current Team will be permanently deleted.']" confirmationText="{{ $team }}"
|
||||||
buttonTitle="Delete Team"
|
|
||||||
isErrorButton
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="['The current Team will be permanently deleted.']"
|
|
||||||
confirmationText="{{ $team }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
|
||||||
shortConfirmationLabel="Team Name"
|
shortConfirmationLabel="Team Name" step3ButtonText="Permanently Delete" />
|
||||||
step3ButtonText="Permanently Delete"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -26,6 +26,9 @@
|
|||||||
<div class="pb-4">Code source of your application.</div>
|
<div class="pb-4">Code source of your application.</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
|
<div>Currently connected source: <span
|
||||||
|
class="font-bold text-warning">{{ $application->source->name }}</span>
|
||||||
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input placeholder="coollabsio/coolify-example" id="gitRepository" label="Repository" />
|
<x-forms.input placeholder="coollabsio/coolify-example" id="gitRepository" label="Repository" />
|
||||||
<x-forms.input placeholder="main" id="gitBranch" label="Branch" />
|
<x-forms.input placeholder="main" id="gitBranch" label="Branch" />
|
||||||
@@ -34,6 +37,39 @@
|
|||||||
<x-forms.input placeholder="HEAD" id="gitCommitSha" placeholder="HEAD" label="Commit SHA" />
|
<x-forms.input placeholder="HEAD" id="gitCommitSha" placeholder="HEAD" label="Commit SHA" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-4">
|
||||||
|
<h3 class="pb-2">Change Git Source</h3>
|
||||||
|
<div class="grid grid-cols-1 gap-2">
|
||||||
|
@forelse ($sources as $source)
|
||||||
|
<div wire:key="{{ $source->name }}">
|
||||||
|
<x-modal-confirmation title="Change Git Source" :actions="['Change git source to ' . $source->name]" :buttonFullWidth="true"
|
||||||
|
:isHighlightedButton="$application->source_id === $source->id" :disabled="$application->source_id === $source->id"
|
||||||
|
submitAction="changeSource({{ $source->id }}, {{ $source->getMorphClass() }})"
|
||||||
|
:confirmWithText="true" confirmationText="Change Git Source"
|
||||||
|
confirmationLabel="Please confirm changing the git source by entering the text below"
|
||||||
|
shortConfirmationLabel="Confirmation Text" :confirmWithPassword="false">
|
||||||
|
<x-slot:customButton>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="box-title">
|
||||||
|
{{ $source->name }}
|
||||||
|
@if ($application->source_id === $source->id)
|
||||||
|
<span class="text-xs">(current)</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="box-description">
|
||||||
|
{{ $source->organization ?? 'Personal Account' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-slot:customButton>
|
||||||
|
</x-modal-confirmation>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div class="text-center">No sources found</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if ($privateKeyId)
|
@if ($privateKeyId)
|
||||||
<h3 class="pt-4">Deploy Key</h3>
|
<h3 class="pt-4">Deploy Key</h3>
|
||||||
<div class="py-2 pt-4">Currently attached Private Key: <span
|
<div class="py-2 pt-4">Currently attached Private Key: <span
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
wire:model="contents.{{ $fileName }}" rows="5" />
|
wire:model="contents.{{ $fileName }}" rows="5" />
|
||||||
@else
|
@else
|
||||||
<livewire:server.proxy.dynamic-configuration-navbar :server_id="$server->id"
|
<livewire:server.proxy.dynamic-configuration-navbar :server_id="$server->id"
|
||||||
:fileName="$fileName" :value="$value" :newFile="false"
|
:fileName="$fileName" :value="$value ?? ''" :newFile="false"
|
||||||
wire:key="{{ $fileName }}-{{ $loop->index }}" />
|
wire:key="{{ $fileName }}-{{ $loop->index }}" />
|
||||||
<x-forms.textarea disabled wire:model="contents.{{ $fileName }}"
|
<x-forms.textarea disabled wire:model="contents.{{ $fileName }}"
|
||||||
rows="10" />
|
rows="10" />
|
||||||
|
@@ -277,12 +277,15 @@
|
|||||||
emails: 'read',
|
emails: 'read',
|
||||||
administration: 'read'
|
administration: 'read'
|
||||||
};
|
};
|
||||||
|
const default_events = ['push'];
|
||||||
if (preview_deployment_permissions) {
|
if (preview_deployment_permissions) {
|
||||||
default_permissions.pull_requests = 'write';
|
default_permissions.pull_requests = 'write';
|
||||||
|
default_events.push('pull_request');
|
||||||
}
|
}
|
||||||
if (administration) {
|
if (administration) {
|
||||||
default_permissions.administration = 'write';
|
default_permissions.administration = 'write';
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
name,
|
name,
|
||||||
url: baseUrl,
|
url: baseUrl,
|
||||||
@@ -297,7 +300,7 @@
|
|||||||
setup_url: `${webhookBaseUrl}/source/github/install?source=${uuid}`,
|
setup_url: `${webhookBaseUrl}/source/github/install?source=${uuid}`,
|
||||||
setup_on_update: true,
|
setup_on_update: true,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
default_events: ['pull_request', 'push']
|
default_events
|
||||||
};
|
};
|
||||||
const form = document.createElement('form');
|
const form = document.createElement('form');
|
||||||
form.setAttribute('method', 'post');
|
form.setAttribute('method', 'post');
|
||||||
|
@@ -3,37 +3,62 @@
|
|||||||
Subscribe | Coolify
|
Subscribe | Coolify
|
||||||
</x-slot>
|
</x-slot>
|
||||||
@if (auth()->user()->isAdminFromSession())
|
@if (auth()->user()->isAdminFromSession())
|
||||||
<div>
|
@if (request()->query->get('cancelled'))
|
||||||
<div class="flex gap-2">
|
<div class="mb-6 rounded alert-error">
|
||||||
<h1>Subscriptions</h1>
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||||
@if (subscriptionProvider() === 'stripe' && $alreadySubscribed)
|
viewBox="0 0 24 24">
|
||||||
<x-forms.button wire:click='stripeCustomerPortal'>Manage My Subscription</x-forms.button>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
@endif
|
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<span>Something went wrong with your subscription. Please try again or contact
|
||||||
|
support.</span>
|
||||||
</div>
|
</div>
|
||||||
@if (request()->query->get('cancelled'))
|
@endif
|
||||||
<div class="mb-6 rounded alert-error">
|
<div class="flex gap-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
<h1>Subscriptions</h1>
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
<span>Something went wrong with your subscription. Please try again or contact
|
|
||||||
support.</span>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if (config('subscription.provider') === 'stripe')
|
|
||||||
<livewire:subscription.pricing-plans />
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
@if ($loading)
|
||||||
|
<div class="flex gap-2" wire:init="getStripeStatus">
|
||||||
|
Loading your subscription status...
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
@if ($isUnpaid)
|
||||||
|
<div class="mb-6 rounded alert-error">
|
||||||
|
<span>Your last payment was failed for Coolify Cloud.</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="mb-2">Open the following link, navigate to the button and pay your unpaid/past due
|
||||||
|
subscription.
|
||||||
|
</p>
|
||||||
|
<x-forms.button wire:click='stripeCustomerPortal'>Billing Portal</x-forms.button>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
@if (config('subscription.provider') === 'stripe')
|
||||||
|
<div @class([
|
||||||
|
'pb-4' => $isCancelled,
|
||||||
|
'pb-10' => !$isCancelled,
|
||||||
|
])>
|
||||||
|
@if ($isCancelled)
|
||||||
|
<div class="alert-error">
|
||||||
|
<span>It looks like your previous subscription has been cancelled, because you forgot to
|
||||||
|
pay
|
||||||
|
the bills.<br />Please subscribe again to continue using Coolify.</span>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<livewire:subscription.pricing-plans />
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
@else
|
@else
|
||||||
<div class="flex flex-col justify-center mx-10">
|
<div class="flex flex-col justify-center mx-10">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<h1>Subscription</h1>
|
<h1>Subscription</h1>
|
||||||
</div>
|
</div>
|
||||||
<div>You are not an admin or have been removed from this team. If this does not make sense, please <span
|
<div>You are not an admin so you cannot manage your Team's subscription. If this does not make sense, please
|
||||||
class="underline cursor-pointer dark:text-white" wire:click="help">contact
|
<span class="underline cursor-pointer dark:text-white" wire:click="help">contact
|
||||||
us</span>.</div>
|
us</span>.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div x-data="{ selected: 'monthly' }" class="w-full pb-20 pt-10">
|
<div x-data="{ selected: 'monthly' }" class="w-full pb-20">
|
||||||
<div class="px-6 mx-auto lg:px-8">
|
<div class="px-6 mx-auto lg:px-8">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<fieldset
|
<fieldset
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flow-root mt-12">
|
<div class="flow-root mt-12">
|
||||||
<div
|
<div
|
||||||
class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-neutral-200 dark:divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-1 lg:divide-x lg:divide-y-0 xl:-mx-4">
|
class="grid grid-cols-1 -mt-16 divide-y divide-neutral-200 dark:divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-1 lg:divide-x lg:divide-y-0 xl:-mx-4">
|
||||||
<div class="pt-16 lg:px-8 lg:pt-0 xl:px-14">
|
<div class="pt-16 lg:px-8 lg:pt-0 xl:px-14">
|
||||||
<h3 id="tier-dynamic" class="text-4xl font-semibold leading-7 dark:text-white">Pay-as-you-go</h3>
|
<h3 id="tier-dynamic" class="text-4xl font-semibold leading-7 dark:text-white">Pay-as-you-go</h3>
|
||||||
<p class="mt-4 text-sm leading-6 dark:text-neutral-400">
|
<p class="mt-4 text-sm leading-6 dark:text-neutral-400">
|
||||||
@@ -72,14 +72,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic"
|
<div class="flex pt-4 h-14">
|
||||||
class="w-full h-10 buyme" wire:click="subscribeStripe('dynamic-monthly')">
|
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic"
|
||||||
Subscribe
|
class="w-full" wire:click="subscribeStripe('dynamic-monthly')">
|
||||||
</x-forms.button>
|
Subscribe
|
||||||
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic"
|
</x-forms.button>
|
||||||
class="w-full h-10 buyme" wire:click="subscribeStripe('dynamic-yearly')">
|
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic"
|
||||||
Subscribe
|
class="w-full" wire:click="subscribeStripe('dynamic-yearly')">
|
||||||
</x-forms.button>
|
Subscribe
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
<ul role="list" class="mt-8 space-y-3 text-sm leading-6 dark:text-neutral-400">
|
<ul role="list" class="mt-8 space-y-3 text-sm leading-6 dark:text-neutral-400">
|
||||||
<li class="flex">
|
<li class="flex">
|
||||||
<svg class="flex-none w-5 h-6 mr-3 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
<svg class="flex-none w-5 h-6 mr-3 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# documentation: https://docs.deno.com/deploy/kv/manual/
|
# documentation: https://docs.deno.com/deploy/kv/manual/
|
||||||
# slogan: The Denoland key-value database
|
# slogan: The Denoland key-value database
|
||||||
# tags: deno, kv, key-value, database
|
# tags: deno, kv, key-value, database
|
||||||
# logo: svgs/denoKV.svg
|
# logo: svgs/denokv.svg
|
||||||
# port: 4512
|
# port: 4512
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
@@ -126,6 +126,7 @@ services:
|
|||||||
- S3_SECRET_KEY=${S3_SECRET_KEY:-}
|
- S3_SECRET_KEY=${S3_SECRET_KEY:-}
|
||||||
- S3_BUCKET=${S3_BUCKET:-evolution}
|
- S3_BUCKET=${S3_BUCKET:-evolution}
|
||||||
- S3_PORT=${S3_PORT:-443}
|
- S3_PORT=${S3_PORT:-443}
|
||||||
|
- S3_REGION=${S3_REGION:-us-east-1}
|
||||||
- S3_ENDPOINT=${S3_ENDPOINT:-files.site.com}
|
- S3_ENDPOINT=${S3_ENDPOINT:-files.site.com}
|
||||||
- S3_USE_SSL=${S3_USE_SSL:-true}
|
- S3_USE_SSL=${S3_USE_SSL:-true}
|
||||||
- 'AUTHENTICATION_API_KEY=${SERVICE_PASSWORD_AUTHENTICATIONAPIKEY}'
|
- 'AUTHENTICATION_API_KEY=${SERVICE_PASSWORD_AUTHENTICATIONAPIKEY}'
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
odoo:
|
odoo:
|
||||||
image: odoo:17
|
image: odoo:18
|
||||||
environment:
|
environment:
|
||||||
- SERVICE_FQDN_ODOO_8069
|
- SERVICE_FQDN_ODOO_8069
|
||||||
- HOST=postgresql
|
- HOST=postgresql
|
||||||
@@ -14,6 +14,7 @@ services:
|
|||||||
- PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
- PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||||
volumes:
|
volumes:
|
||||||
- odoo-web-data:/var/lib/odoo
|
- odoo-web-data:/var/lib/odoo
|
||||||
|
- odoo-extra-addons:/mnt/extra-addons
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8069"]
|
test: ["CMD", "curl", "-f", "http://127.0.0.1:8069"]
|
||||||
interval: 2s
|
interval: 2s
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.407"
|
"version": "4.0.0-beta.408"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.408"
|
"version": "4.0.0-beta.410"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.8"
|
"version": "1.0.8"
|
||||||
},
|
},
|
||||||
"realtime": {
|
"realtime": {
|
||||||
"version": "1.0.6"
|
"version": "1.0.7"
|
||||||
},
|
},
|
||||||
"sentinel": {
|
"sentinel": {
|
||||||
"version": "0.0.15"
|
"version": "0.0.15"
|
||||||
|
Reference in New Issue
Block a user