Merge pull request #1612 from coollabsio/next

v4.0.0-beta.184
This commit is contained in:
Andras Bacsai 2024-01-09 14:58:01 +01:00 committed by GitHub
commit 36931b5b18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
145 changed files with 1996 additions and 1484 deletions

View File

@ -2,11 +2,13 @@
namespace App\Http\Controllers;
use App\Events\TestEvent;
use App\Models\InstanceSettings;
use App\Models\S3Storage;
use App\Models\StandalonePostgresql;
use App\Models\TeamInvitation;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
@ -14,11 +16,55 @@
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
use Illuminate\Support\Facades\Password;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
public function realtime_test() {
if (auth()->user()?->currentTeam()->id !== 0) {
return redirect(RouteServiceProvider::HOME);
}
TestEvent::dispatch();
return 'Look at your other tab.';
}
public function verify() {
return view('auth.verify-email');
}
public function email_verify() {
request()->fulfill();
$name = request()->user()?->name;
send_internal_notification("User {$name} verified their email address.");
return redirect(RouteServiceProvider::HOME);
}
public function forgot_password() {
if (is_transactional_emails_active()) {
$arrayOfRequest = request()->only(Fortify::email());
request()->merge([
'email' => Str::lower($arrayOfRequest['email']),
]);
$type = set_transanctional_email_settings();
if (!$type) {
return response()->json(['message' => 'Transactional emails are not active'], 400);
}
request()->validate([Fortify::email() => 'required|email']);
$status = Password::broker(config('fortify.passwords'))->sendResetLink(
request()->only(Fortify::email())
);
if ($status == Password::RESET_LINK_SENT) {
return app(SuccessfulPasswordResetLinkRequestResponse::class, ['status' => $status]);
}
if ($status == Password::RESET_THROTTLED) {
return response('Already requested a password reset in the past minutes.', 400);
}
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
}
return response()->json(['message' => 'Transactional emails are not active'], 400);
}
public function link()
{
$token = request()->get('token');
@ -51,90 +97,7 @@ public function link()
return redirect()->route('login')->with('error', 'Invalid credentials.');
}
public function license()
{
if (!isCloud()) {
abort(404);
}
return view('settings.license', [
'settings' => InstanceSettings::get(),
]);
}
public function force_passoword_reset()
{
return view('auth.force-password-reset');
}
public function boarding()
{
if (currentTeam()->boarding || isDev()) {
return view('boarding');
} else {
return redirect()->route('dashboard');
}
}
public function settings()
{
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$database = StandalonePostgresql::whereName('coolify-db')->first();
if ($database) {
if ($database->status !== 'running') {
$database->status = 'running';
$database->save();
}
$s3s = S3Storage::whereTeamId(0)->get();
}
return view('settings.configuration', [
'settings' => $settings,
'database' => $database,
's3s' => $s3s ?? [],
]);
} else {
return redirect()->route('dashboard');
}
}
public function team()
{
$invitations = [];
if (auth()->user()->isAdminFromSession()) {
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
return view('team.index', [
'invitations' => $invitations,
]);
}
public function storages()
{
$s3 = S3Storage::ownedByCurrentTeam()->get();
return view('team.storages.all', [
's3' => $s3,
]);
}
public function storages_show()
{
$storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->firstOrFail();
return view('team.storages.show', [
'storage' => $storage,
]);
}
public function members()
{
$invitations = [];
if (auth()->user()->isAdminFromSession()) {
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
return view('team.members', [
'invitations' => $invitations,
]);
}
public function acceptInvitation()
public function accept_invitation()
{
try {
$resetPassword = request()->query('reset-password');
@ -169,7 +132,7 @@ public function acceptInvitation()
}
}
public function revokeInvitation()
public function revoke_invitation()
{
try {
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();

View File

@ -1,84 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
class DatabaseController extends Controller
{
use AuthorizesRequests, ValidatesRequests;
public function configuration()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
return view('project.database.configuration', ['database' => $database]);
}
public function executions()
{
$backup_uuid = request()->route('backup_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
if (!$backup) {
return redirect()->route('dashboard');
}
$executions = collect($backup->executions)->sortByDesc('created_at');
return view('project.database.backups.executions', [
'database' => $database,
'backup' => $backup,
'executions' => $executions,
's3s' => currentTeam()->s3s,
]);
}
public function backups()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
// No backups for redis
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
return redirect()->route('project.database.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'database_uuid' => $database->uuid,
]);
}
return view('project.database.backups.all', [
'database' => $database,
's3s' => currentTeam()->s3s,
]);
}
}

View File

@ -30,6 +30,7 @@
use Symfony\Component\Yaml\Yaml;
use Throwable;
use Visus\Cuid2\Cuid2;
use Yosymfony\Toml\Toml;
class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
{
@ -73,6 +74,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
private $env_args;
private $docker_compose;
private $docker_compose_base64;
private ?string $nixpacks_plan = null;
private ?string $nixpacks_type = null;
private string $dockerfile_location = '/Dockerfile';
private string $docker_compose_location = '/docker-compose.yml';
private ?string $docker_compose_custom_start_command = null;
@ -170,7 +173,7 @@ public function handle(): void
if ($containerName === 'coolify-proxy') {
continue;
}
if (preg_match('/-(\d{12})/',$containerName)) {
if (preg_match('/-(\d{12})/', $containerName)) {
continue;
}
$containerIp = data_get($container, 'IPv4Address');
@ -347,6 +350,9 @@ private function just_restart()
$this->generate_image_names();
$this->check_image_locally_or_remotely();
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
$this->execute_remote_command([
"echo 'Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.'",
]);
$this->create_workdir();
$this->generate_compose_file();
$this->rolling_update();
@ -579,7 +585,7 @@ private function deploy_nixpacks_buildpack()
$this->generate_nixpacks_confs();
$this->generate_compose_file();
$this->generate_build_env_variables();
$this->add_build_env_variables_to_dockerfile();
// $this->add_build_env_variables_to_dockerfile();
$this->build_image();
$this->rolling_update();
}
@ -601,6 +607,24 @@ private function deploy_static_buildpack()
$this->rolling_update();
}
private function framework_based_notification()
{
// Laravel old env variables
if ($this->pull_request_id === 0) {
$nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
} else {
$nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
}
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
$this->execute_remote_command(
[
"echo 'There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements'", 'type' => 'err'
],
);
}
}
private function rolling_update()
{
if ($this->server->isSwarm()) {
@ -637,6 +661,7 @@ private function rolling_update()
$this->application_deployment_queue->addLogEntry("Rolling update completed.");
}
}
$this->framework_based_notification();
}
private function health_check()
{
@ -676,7 +701,7 @@ private function health_check()
"echo 'Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}'"
],
);
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
$this->newVersionIsHealthy = true;
$this->application->update(['status' => 'running']);
$this->execute_remote_command(
@ -686,6 +711,10 @@ private function health_check()
);
break;
}
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') {
$this->newVersionIsHealthy = false;
break;
}
$counter++;
sleep($this->application->health_check_interval);
}
@ -870,20 +899,28 @@ private function generate_nixpacks_confs()
[
"echo -n 'Generating nixpacks configuration with: $nixpacks_command'",
],
[executeInDocker($this->deployment_uuid, $nixpacks_command)],
[executeInDocker($this->deployment_uuid, "cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
[executeInDocker($this->deployment_uuid, "rm -f {$this->workdir}/.nixpacks/Dockerfile")]
[executeInDocker($this->deployment_uuid, $nixpacks_command), "save" => "nixpacks_plan", "hidden" => true],
[executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), "save" => "nixpacks_type", "hidden" => true],
);
if ($this->saved_outputs->get('nixpacks_type')) {
$this->nixpacks_type = $this->saved_outputs->get('nixpacks_type');
}
if ($this->saved_outputs->get('nixpacks_plan')) {
$this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
if ($this->nixpacks_plan) {
$parsed = Toml::Parse($this->nixpacks_plan);
// Do any modifications here
$cmds = collect(data_get($parsed, 'phases.setup.cmds', []));
data_set($parsed, 'phases.setup.cmds', $cmds);
$this->nixpacks_plan = json_encode($parsed);
}
}
}
private function nixpacks_build_cmd()
{
$this->generate_env_variables();
// $cacheKey = $this->application->uuid;
// if ($this->pull_request_id !== 0) {
// $cacheKey = "{$this->application->uuid}-pr-{$this->pull_request_id}";
// }
$nixpacks_command = "nixpacks build {$this->env_args} --no-error-without-start";
$nixpacks_command = "nixpacks plan -f toml {$this->env_args}";
if ($this->application->build_command) {
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
}
@ -893,7 +930,7 @@ private function nixpacks_build_cmd()
if ($this->application->install_command) {
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
}
$nixpacks_command .= " -o {$this->workdir} {$this->workdir}";
$nixpacks_command .= " {$this->workdir}";
return $nixpacks_command;
}
@ -904,10 +941,16 @@ private function generate_env_variables()
foreach ($this->application->nixpacks_environment_variables as $env) {
$this->env_args->push("--env {$env->key}={$env->value}");
}
foreach ($this->application->build_environment_variables as $env) {
$this->env_args->push("--env {$env->key}={$env->value}");
}
} else {
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
$this->env_args->push("--env {$env->key}={$env->value}");
}
foreach ($this->application->build_environment_variables_preview as $env) {
$this->env_args->push("--env {$env->key}={$env->value}");
}
}
$this->env_args = $this->env_args->implode(' ');
@ -1231,22 +1274,28 @@ private function build_image()
}");
} else {
if ($this->application->build_pack === 'nixpacks') {
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "cp {$this->workdir}/Dockerfile {$this->workdir}/.nixpacks/Dockerfile")
],
);
}
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > {$this->workdir}/thegameplan.json"), "hidden" => true]);
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
]);
}
// }
} else {
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
}
}
$dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/
@ -1279,33 +1328,39 @@ private function build_image()
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
],
[
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]
);
} else {
// Pure Dockerfile based deployment
if ($this->application->dockerfile) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
if ($this->application->build_pack === 'nixpacks') {
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "cp {$this->workdir}/Dockerfile {$this->workdir}/.nixpacks/Dockerfile")
],
);
}
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > {$this->workdir}/thegameplan.json"), "hidden" => true]);
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
}
} else {
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
}
}
}
}
$this->execute_remote_command([

View File

@ -162,6 +162,10 @@ public function handle()
// Notify user that this container should not be there.
}
}
if (data_get($container,'Name') === '/coolify-db') {
$foundDatabases[] = 0;
}
}
$serviceLabelId = data_get($labels, 'coolify.serviceId');
if ($serviceLabelId) {
@ -212,7 +216,7 @@ public function handle()
}
$name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
$containerName = $name ? "$name, available at $fqdn" : $fqdn;
$projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name');

View File

@ -274,7 +274,7 @@ public function showNewResource()
{
$this->skipBoarding();
return redirect()->route(
'project.resources.new',
'project.resource.create',
[
'project_uuid' => $this->createdProject->uuid,
'environment_name' => 'production',

View File

@ -0,0 +1,18 @@
<?php
namespace App\Livewire\CommandCenter;
use App\Models\Server;
use Livewire\Component;
class Index extends Component
{
public $servers = [];
public function mount() {
$this->servers = Server::isReachable()->get();
}
public function render()
{
return view('livewire.command-center.index');
}
}

View File

@ -22,6 +22,10 @@ public function mount()
{
$this->email = auth()->user()->email;
}
public function render()
{
return view('livewire.force-password-reset')->layout('layouts.simple');
}
public function submit()
{
try {

View File

@ -5,21 +5,19 @@
use Livewire\Attributes\Validate;
use Livewire\Component;
class Form extends Component
class Index extends Component
{
public int $userId;
public string $email;
#[Validate('required')]
public string $name;
public function mount()
{
$this->userId = auth()->user()->id;
$this->name = auth()->user()->name;
$this->email = auth()->user()->email;
}
public function submit()
{
@ -34,4 +32,8 @@ public function submit()
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.profile.index');
}
}

View File

@ -27,7 +27,7 @@ public function submit()
'project_id' => $this->project->id,
]);
return redirect()->route('project.resources', [
return redirect()->route('project.resource.index', [
'project_uuid' => $this->project->uuid,
'environment_name' => $environment->name,
]);

View File

@ -4,7 +4,6 @@
use App\Models\Application;
use App\Models\Server;
use App\Models\StandaloneDocker;
use Livewire\Component;
class Configuration extends Component

View File

@ -1,15 +1,15 @@
<?php
namespace App\Livewire\Project\Application;
namespace App\Livewire\Project\Application\Deployment;
use App\Models\Application;
use Illuminate\Support\Collection;
use Livewire\Component;
class Deployments extends Component
class Index extends Component
{
public Application $application;
public Array|Collection $deployments = [];
public array|Collection $deployments = [];
public int $deployments_count = 0;
public string $current_url;
public int $skip = 0;
@ -19,11 +19,28 @@ class Deployments extends Component
protected $queryString = ['pull_request_id'];
public function mount()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
if (!$application) {
return redirect()->route('dashboard');
}
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
$this->application = $application;
$this->deployments = $deployments;
$this->deployments_count = $count;
$this->current_url = url()->current();
$this->show_pull_request_only();
$this->show_more();
}
private function show_pull_request_only() {
private function show_pull_request_only()
{
if ($this->pull_request_id) {
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
}
@ -57,4 +74,8 @@ public function load_deployments(int|null $take = null)
$this->show_pull_request_only();
$this->show_more();
}
public function render()
{
return view('livewire.project.application.deployment.index');
}
}

View File

@ -1,35 +1,20 @@
<?php
namespace App\Http\Controllers;
namespace App\Livewire\Project\Application\Deployment;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Livewire\Component;
class ApplicationController extends Controller
class Show extends Component
{
use AuthorizesRequests, ValidatesRequests;
public Application $application;
public ApplicationDeploymentQueue $application_deployment_queue;
public string $deployment_uuid;
public $isKeepAliveOn = true;
protected $listeners = ['refreshQueue'];
public function deployments()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
if (!$application) {
return redirect()->route('dashboard');
}
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
}
public function deployment()
{
public function mount() {
$deploymentUuid = request()->route('deployment_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
@ -46,7 +31,7 @@ public function deployment()
}
// $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first();
// if (!$activity) {
// return redirect()->route('project.application.deployments', [
// return redirect()->route('project.application.deployment.index', [
// 'project_uuid' => $project->uuid,
// 'environment_name' => $environment->name,
// 'application_uuid' => $application->uuid,
@ -54,17 +39,32 @@ public function deployment()
// }
$application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first();
if (!$application_deployment_queue) {
return redirect()->route('project.application.deployments', [
return redirect()->route('project.application.deployment.index', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'application_uuid' => $application->uuid,
]);
}
return view('project.application.deployment', [
'application' => $application,
// 'activity' => $activity,
'application_deployment_queue' => $application_deployment_queue,
'deployment_uuid' => $deploymentUuid,
]);
$this->application = $application;
$this->application_deployment_queue = $application_deployment_queue;
$this->deployment_uuid = $deploymentUuid;
}
public function refreshQueue()
{
$this->application_deployment_queue->refresh();
}
public function polling()
{
$this->dispatch('deploymentFinished');
$this->application_deployment_queue->refresh();
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
$this->isKeepAliveOn = false;
}
}
public function render()
{
return view('livewire.project.application.deployment.show');
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Livewire\Project\Application;
use App\Models\ApplicationDeploymentQueue;
use Livewire\Component;
class DeploymentLogs extends Component
{
public ApplicationDeploymentQueue $application_deployment_queue;
public $isKeepAliveOn = true;
protected $listeners = ['refreshQueue'];
public function refreshQueue()
{
$this->application_deployment_queue->refresh();
}
public function polling()
{
$this->dispatch('deploymentFinished');
$this->application_deployment_queue->refresh();
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
$this->isKeepAliveOn = false;
}
}
}

View File

@ -39,7 +39,7 @@ public function check_status($showNotification = false)
} else {
dispatch(new ServerStatusJob($this->application->destination->server));
}
if ($showNotification) $this->dispatch('success', 'Application status updated.');
if ($showNotification) $this->dispatch('success', "Application ({$this->application->name}) status updated.");
}
public function force_deploy_without_cache()
@ -60,7 +60,7 @@ public function deployNew()
force_rebuild: false,
is_new_deployment: true,
);
return redirect()->route('project.application.deployment', [
return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
@ -83,7 +83,7 @@ public function deploy(bool $force_rebuild = false)
deployment_uuid: $this->deploymentUuid,
force_rebuild: $force_rebuild,
);
return redirect()->route('project.application.deployment', [
return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
@ -113,7 +113,7 @@ public function restartNew()
restart_only: true,
is_new_deployment: true,
);
return redirect()->route('project.application.deployment', [
return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,
@ -128,7 +128,7 @@ public function restart()
deployment_uuid: $this->deploymentUuid,
restart_only: true,
);
return redirect()->route('project.application.deployment', [
return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid,

View File

@ -52,7 +52,7 @@ public function deploy(int $pull_request_id, string|null $pull_request_html_url
force_rebuild: true,
pull_request_id: $pull_request_id,
);
return redirect()->route('project.application.deployment', [
return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deployment_uuid,

View File

@ -29,7 +29,7 @@ public function rollbackImage($commit)
commit: $commit,
force_rebuild: false,
);
return redirect()->route('project.application.deployment', [
return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $deployment_uuid,

View File

@ -8,7 +8,7 @@
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class CloneProject extends Component
class CloneMe extends Component
{
public string $project_uuid;
public string $environment_name;
@ -41,7 +41,7 @@ public function mount($project_uuid)
public function render()
{
return view('livewire.project.clone-project');
return view('livewire.project.clone-me');
}
public function selectServer($server_id, $destination_id)
@ -152,7 +152,7 @@ public function clone()
}
$newService->parse();
}
return redirect()->route('project.resources', [
return redirect()->route('project.resource.index', [
'project_uuid' => $newProject->uuid,
'environment_name' => $newEnvironment->name,
]);

View File

@ -0,0 +1,41 @@
<?php
namespace App\Livewire\Project\Database\Backup;
use Livewire\Component;
class Execution extends Component
{
public $database;
public $backup;
public $executions;
public $s3s;
public function mount() {
$backup_uuid = request()->route('backup_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
if (!$backup) {
return redirect()->route('dashboard');
}
$executions = collect($backup->executions)->sortByDesc('created_at');
$this->database = $database;
$this->backup = $backup;
$this->executions = $executions;
$this->s3s = currentTeam()->s3s;
}
public function render()
{
return view('livewire.project.database.backup.execution');
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Livewire\Project\Database\Backup;
use Livewire\Component;
class Index extends Component
{
public $database;
public $s3s;
public function mount() {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
// No backups for redis
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
return redirect()->route('project.database.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'database_uuid' => $database->uuid,
]);
}
$this->database = $database;
$this->s3s = currentTeam()->s3s;
}
public function render()
{
return view('livewire.project.database.backup.index');
}
}

View File

@ -52,7 +52,7 @@ public function delete()
$url = $url->getPath() . "#{$url->getFragment()}";
return redirect($url);
} else {
return redirect()->route('project.database.backups.all', $this->parameters);
return redirect()->route('project.database.backup.index', $this->parameters);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Livewire\Project\Database;
use Livewire\Component;
class Configuration extends Component
{
public $database;
public function mount() {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
$this->database = $database;
}
public function render()
{
return view('livewire.project.database.configuration');
}
}

View File

@ -25,6 +25,6 @@ public function delete()
return $this->dispatch('error', 'Project has resources defined, please delete them first.');
}
$project->delete();
return redirect()->route('projects');
return redirect()->route('project.index');
}
}

View File

@ -12,6 +12,15 @@ class Edit extends Component
'project.name' => 'required|min:3|max:255',
'project.description' => 'nullable|string|max:255',
];
public function mount() {
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
$this->project = $project;
}
public function submit()
{

View File

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Project;
use App\Models\Project;
use App\Models\Server;
use Livewire\Component;
class Index extends Component
{
public $projects;
public $servers;
public function mount() {
$this->projects = Project::ownedByCurrentTeam()->get();
$this->servers = Server::ownedByCurrentTeam()->count();
}
public function render()
{
return view('livewire.project.index');
}
}

View File

@ -55,7 +55,7 @@ public function render()
public function updatedSelectedEnvironment()
{
return redirect()->route('project.resources.new', [
return redirect()->route('project.resource.create', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->selectedEnvironment,
]);
@ -157,7 +157,7 @@ public function setServer(Server $server)
public function setDestination(string $destination_uuid)
{
$this->destination_uuid = $destination_uuid;
return redirect()->route('project.resources.new', [
return redirect()->route('project.resource.create', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->parameters['environment_name'],
'type' => $this->type,

View File

@ -1,52 +1,18 @@
<?php
namespace App\Http\Controllers;
namespace App\Livewire\Project\Resource;
use App\Models\EnvironmentVariable;
use App\Models\Project;
use App\Models\Server;
use App\Models\Service;
use App\Models\StandaloneDocker;
use Illuminate\Support\Str;
use Livewire\Component;
class ProjectController extends Controller
class Create extends Component
{
public function all()
{
return view('projects', [
'projects' => Project::ownedByCurrentTeam()->get(),
'servers' => Server::ownedByCurrentTeam()->count(),
]);
}
public function edit()
{
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
return view('project.edit', ['project' => $project]);
}
public function show()
{
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
$project->load(['environments']);
return view('project.show', ['project' => $project]);
}
public function new()
{
public $type;
public function mount() {
$services = getServiceTemplates();
$type = Str::of(request()->query('type'));
$type = str(request()->query('type'));
$destination_uuid = request()->query('destination');
$server_id = request()->query('server_id');
@ -81,14 +47,14 @@ public function new()
$oneClickService = data_get($services, "$oneClickServiceName.compose");
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
if ($oneClickDotEnvs) {
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
return !empty($value);
});
}
if ($oneClickService) {
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
$service = Service::create([
'name' => "$oneClickServiceName-" . Str::random(10),
'name' => "$oneClickServiceName-" . str()->random(10),
'docker_compose_raw' => base64_decode($oneClickService),
'environment_id' => $environment->id,
'server_id' => (int) $server_id,
@ -99,8 +65,8 @@ public function new()
$service->save();
if ($oneClickDotEnvs?->count() > 0) {
$oneClickDotEnvs->each(function ($value) use ($service) {
$key = Str::before($value, '=');
$value = Str::of(Str::after($value, '='));
$key = str()->before($value, '=');
$value = str(str()->after($value, '='));
$generatedValue = $value;
if ($value->contains('SERVICE_')) {
$command = $value->after('SERVICE_')->beforeLast('_');
@ -123,24 +89,10 @@ public function new()
]);
}
}
return view('project.new', [
'type' => $type->value()
]);
$this->type = $type->value();
}
public function resources()
public function render()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
if (!$environment) {
return redirect()->route('dashboard');
}
return view('project.resources', [
'project' => $project,
'environment' => $environment
]);
return view('livewire.project.resource.create');
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Livewire\Project\Resource;
use App\Models\Environment;
use App\Models\Project;
use Livewire\Component;
class Index extends Component
{
public Project $project;
public Environment $environment;
public function mount () {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
if (!$environment) {
return redirect()->route('dashboard');
}
$this->project = $project;
$this->environment = $environment;
}
public function render()
{
return view('livewire.project.resource.index');
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace App\Livewire\Project\Service;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use Livewire\Component;
class Configuration extends Component
{
public Service $service;
public $applications;
public $databases;
public array $parameters;
public array $query;
public function getListeners()
{
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
"refreshStacks",
"checkStatus",
];
}
public function render()
{
return view('livewire.project.service.configuration');
}
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$this->applications = $this->service->applications->sort();
$this->databases = $this->service->databases->sort();
}
public function checkStatus()
{
dispatch_sync(new ContainerStatusJob($this->service->server));
$this->refreshStacks();
$this->dispatch('serviceStatusChanged');
}
public function refreshStacks()
{
$this->applications = $this->service->applications->sort();
$this->applications->each(function ($application) {
$application->refresh();
});
$this->databases = $this->service->databases->sort();
$this->databases->each(function ($database) {
$database->refresh();
});
}
}

View File

@ -2,53 +2,51 @@
namespace App\Livewire\Project\Service;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use Illuminate\Support\Collection;
use Livewire\Component;
class Index extends Component
{
public Service $service;
public $applications;
public $databases;
public ?ServiceApplication $serviceApplication = null;
public ?ServiceDatabase $serviceDatabase = null;
public array $parameters;
public array $query;
public function getListeners()
public Collection $services;
public $s3s;
protected $listeners = ['generateDockerCompose'];
public function mount()
{
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
"refreshStacks",
"checkStatus",
];
try {
$this->services = collect([]);
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase->getFilesFromServer();
}
$this->s3s = currentTeam()->s3s;
} catch(\Throwable $e) {
return handleError($e, $this);
}
}
public function generateDockerCompose()
{
$this->service->parse();
}
public function render()
{
return view('livewire.project.service.index');
}
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$this->applications = $this->service->applications->sort();
$this->databases = $this->service->databases->sort();
}
public function checkStatus()
{
dispatch_sync(new ContainerStatusJob($this->service->server));
$this->refreshStacks();
$this->dispatch('serviceStatusChanged');
}
public function refreshStacks()
{
$this->applications = $this->service->applications->sort();
$this->applications->each(function ($application) {
$application->refresh();
});
$this->databases = $this->service->databases->sort();
$this->databases->each(function ($database) {
$database->refresh();
});
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace App\Livewire\Project\Service;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use Illuminate\Support\Collection;
use Livewire\Component;
class Show extends Component
{
public Service $service;
public ?ServiceApplication $serviceApplication = null;
public ?ServiceDatabase $serviceDatabase = null;
public array $parameters;
public array $query;
public Collection $services;
public $s3s;
protected $listeners = ['generateDockerCompose'];
public function mount()
{
try {
$this->services = collect([]);
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase->getFilesFromServer();
}
$this->s3s = currentTeam()->s3s;
} catch(\Throwable $e) {
return handleError($e, $this);
}
}
public function generateDockerCompose()
{
$this->service->parse();
}
public function render()
{
return view('livewire.project.service.show');
}
}

View File

@ -25,7 +25,7 @@ public function delete()
{
try {
DeleteResourceJob::dispatchSync($this->resource);
return redirect()->route('project.resources', [
return redirect()->route('project.resource.index', [
'project_uuid' => $this->projectUuid,
'environment_name' => $this->environmentName
]);

View File

@ -10,7 +10,6 @@
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Support\Sleep;
use Livewire\Component;
class ExecuteContainerCommand extends Component

View File

@ -0,0 +1,26 @@
<?php
namespace App\Livewire\Project;
use App\Models\Project;
use Livewire\Component;
class Show extends Component
{
public Project $project;
public function mount() {
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
$project->load(['environments']);
$this->project = $project;
}
public function render()
{
return view('livewire.project.show');
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace App\Livewire\PrivateKey;
namespace App\Livewire\Security\PrivateKey;
use App\Models\PrivateKey;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;

View File

@ -1,11 +1,11 @@
<?php
namespace App\Livewire\PrivateKey;
namespace App\Livewire\Security\PrivateKey;
use App\Models\PrivateKey;
use Livewire\Component;
class Change extends Component
class Show extends Component
{
public PrivateKey $private_key;
public $public_key;
@ -24,6 +24,7 @@ class Change extends Component
public function mount()
{
try {
$this->private_key = PrivateKey::ownedByCurrentTeam(['name', 'description', 'private_key', 'is_git_related'])->whereUuid(request()->private_key_uuid)->firstOrFail();
$this->public_key = $this->private_key->publicKey();
}catch(\Throwable $e) {
return handleError($e, $this);

View File

@ -19,7 +19,7 @@ public function delete()
return;
}
$this->server->delete();
return redirect()->route('server.all');
return redirect()->route('server.index');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@ -15,7 +15,7 @@ public function mount()
try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
return redirect()->route('server.index');
}
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@ -6,7 +6,7 @@
use Illuminate\Database\Eloquent\Collection;
use Livewire\Component;
class All extends Component
class Index extends Component
{
public ?Collection $servers = null;
@ -15,6 +15,6 @@ public function mount () {
}
public function render()
{
return view('livewire.server.all');
return view('livewire.server.index');
}
}

View File

@ -43,7 +43,7 @@ public function mount()
try {
$server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($server)) {
return redirect()->route('server.all');
return redirect()->route('server.index');
}
$this->server = $server;
} catch (\Throwable $e) {

View File

@ -17,7 +17,7 @@ public function mount()
try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
return redirect()->route('server.index');
}
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
} catch (\Throwable $e) {

View File

@ -15,7 +15,7 @@ public function mount()
try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
return redirect()->route('server.index');
}
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@ -20,7 +20,7 @@ public function mount()
try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
return redirect()->route('server.index');
}
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@ -17,7 +17,7 @@ public function mount()
try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
return redirect()->route('server.index');
}
} catch (\Throwable $e) {

View File

@ -0,0 +1,38 @@
<?php
namespace App\Livewire\Settings;
use App\Models\InstanceSettings;
use App\Models\S3Storage;
use App\Models\StandalonePostgresql;
use Livewire\Component;
class Index extends Component
{
public InstanceSettings $settings;
public StandalonePostgresql $database;
public $s3s;
public function mount()
{
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$database = StandalonePostgresql::whereName('coolify-db')->first();
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
if ($database) {
if ($database->status !== 'running') {
$database->status = 'running';
$database->save();
}
$this->database = $database;
}
$this->settings = $settings;
$this->s3s = $s3s;
} else {
return redirect()->route('dashboard');
}
}
public function render()
{
return view('livewire.settings.index');
}
}

View File

@ -1,15 +1,16 @@
<?php
namespace App\Livewire;
namespace App\Livewire\Settings;
use App\Actions\License\CheckResaleLicense;
use App\Models\InstanceSettings;
use Livewire\Component;
class CheckLicense extends Component
class License extends Component
{
public InstanceSettings|null $settings = null;
public InstanceSettings $settings;
public string|null $instance_id = null;
protected $rules = [
'settings.resale_license' => 'nullable',
'settings.is_resale_license_active' => 'nullable',
@ -20,12 +21,17 @@ class CheckLicense extends Component
'settings.is_resale_license_active' => 'Is License Active',
];
public function mount()
{
public function mount () {
if (!isCloud()) {
abort(404);
}
$this->instance_id = config('app.id');
$this->settings = InstanceSettings::get();
}
public function render()
{
return view('livewire.settings.license')->layout('layouts.subscription');
}
public function submit()
{
$this->validate();

View File

@ -6,7 +6,7 @@
use App\Providers\RouteServiceProvider;
use Livewire\Component;
class Show extends Component
class Index extends Component
{
public InstanceSettings $settings;
public bool $alreadySubscribed = false;
@ -26,6 +26,6 @@ public function stripeCustomerPortal() {
}
public function render()
{
return view('livewire.subscription.show')->layout('layouts.subscription');
return view('livewire.subscription.index')->layout('layouts.subscription');
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Livewire\Team;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class Delete extends Component
{
public function delete()
{
$currentTeam = currentTeam();
$currentTeam->delete();
$currentTeam->members->each(function ($user) use ($currentTeam) {
if ($user->id === auth()->user()->id) {
return;
}
$user->teams()->detach($currentTeam);
$session = DB::table('sessions')->where('user_id', $user->id)->first();
if ($session) {
DB::table('sessions')->where('id', $session->id)->delete();
}
});
refreshSession();
return redirect()->route('team.index');
}
}

View File

@ -1,35 +0,0 @@
<?php
namespace App\Livewire\Team;
use App\Models\Team;
use Livewire\Component;
class Form extends Component
{
public Team $team;
protected $rules = [
'team.name' => 'required|min:3|max:255',
'team.description' => 'nullable|min:3|max:255',
];
protected $validationAttributes = [
'team.name' => 'name',
'team.description' => 'description',
];
public function mount()
{
$this->team = currentTeam();
}
public function submit()
{
$this->validate();
try {
$this->team->save();
refreshSession();
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace App\Livewire\Team;
use App\Models\Team;
use App\Models\TeamInvitation;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class Index extends Component
{
public $invitations = [];
public Team $team;
protected $rules = [
'team.name' => 'required|min:3|max:255',
'team.description' => 'nullable|min:3|max:255',
];
protected $validationAttributes = [
'team.name' => 'name',
'team.description' => 'description',
];
public function mount() {
$this->team = currentTeam();
if (auth()->user()->isAdminFromSession()) {
$this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
}
public function render()
{
return view('livewire.team.index');
}
public function submit()
{
$this->validate();
try {
$this->team->save();
refreshSession();
$this->dispatch('success', 'Team updated successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function delete()
{
$currentTeam = currentTeam();
$currentTeam->delete();
$currentTeam->members->each(function ($user) use ($currentTeam) {
if ($user->id === auth()->user()->id) {
return;
}
$user->teams()->detach($currentTeam);
$session = DB::table('sessions')->where('user_id', $user->id)->first();
if ($session) {
DB::table('sessions')->where('id', $session->id)->delete();
}
});
refreshSession();
return redirect()->route('team.index');
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Livewire\Team\Member;
use App\Models\TeamInvitation;
use Livewire\Component;
class Index extends Component
{
public $invitations = [];
public function mount() {
if (auth()->user()->isAdminFromSession()) {
$this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
}
public function render()
{
return view('livewire.team.member.index');
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Team\Notification;
use Livewire\Component;
class Index extends Component
{
public function render()
{
return view('livewire.team.notification.index');
}
}

View File

@ -65,7 +65,7 @@ public function submit()
$this->storage->team_id = currentTeam()->id;
$this->storage->testConnection();
$this->storage->save();
return redirect()->route('team.storages.show', $this->storage->uuid);
return redirect()->route('team.storage.show', $this->storage->uuid);
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@ -43,7 +43,7 @@ public function delete()
{
try {
$this->storage->delete();
return redirect()->route('team.storages.all');
return redirect()->route('team.storage.index');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Livewire\Team\Storage;
use App\Models\S3Storage;
use Livewire\Component;
class Index extends Component
{
public $s3;
public function mount() {
$this->s3 = S3Storage::ownedByCurrentTeam()->get();
}
public function render()
{
return view('livewire.team.storage.index');
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Livewire\Team\Storage;
use App\Models\S3Storage;
use Livewire\Component;
class Show extends Component
{
public $storage = null;
public function mount()
{
$this->storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->first();
if (!$this->storage) {
abort(404);
}
}
public function render()
{
return view('livewire.team.storage.show');
}
}

View File

@ -755,7 +755,6 @@ function rollingUpdateApplication(Server $server, ApplicationDeploymentQueue $de
// if (count($this->ports_mappings_array) > 0) {
// $deployment->addLogEntry('Application has ports mapped to the host system, rolling update is not supported.');
$containers = getCurrentApplicationContainerStatus($server, $this->id, $pullRequestId);
ray($containers);
// if ($pullRequestId === 0) {
// $containers = $containers->filter(function ($container) use ($containerName) {
// return data_get($container, 'Names') !== $containerName;
@ -867,7 +866,6 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id
} else {
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command_base);
}
ray($git_clone_command);
if ($exec_in_docker) {
$commands = collect([
executeInDocker($deployment_uuid, "mkdir -p /root/.ssh"),

View File

@ -48,7 +48,7 @@ public function testConnection(bool $shouldSave = false)
if ($this->unusable_email_sent === false && is_transactional_emails_active()) {
$mail = new MailMessage();
$mail->subject('Coolify: S3 Storage Connection Error');
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('team.storages.show', ['storage_uuid' => $this->uuid])]);
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('team.storage.show', ['storage_uuid' => $this->uuid])]);
$users = collect([]);
$members = $this->team->members()->get();
foreach ($members as $user) {

View File

@ -27,7 +27,7 @@ public function via(object $notifiable): array
public function toMail(): MailMessage
{
$mail = new MailMessage();
$mail->subject("Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}");
$mail->subject("Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}");
$mail->view('emails.container-restarted', [
'containerName' => $this->name,
'serverName' => $this->server->name,
@ -38,12 +38,12 @@ public function toMail(): MailMessage
public function toDiscord(): string
{
$message = "Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}";
$message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
return $message;
}
public function toTelegram(): array
{
$message = "Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}";
$message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
$payload = [
"message" => $message,
];

View File

@ -26,7 +26,7 @@ public function via(object $notifiable): array
public function toMail(): MailMessage
{
$mail = new MailMessage();
$mail->subject("Coolify: A service ({$this->name}) has been stopped on {$this->server->name}");
$mail->subject("Coolify: A resource has been stopped unexpectedly on {$this->server->name}");
$mail->view('emails.container-stopped', [
'containerName' => $this->name,
'serverName' => $this->server->name,
@ -37,12 +37,12 @@ public function toMail(): MailMessage
public function toDiscord(): string
{
$message = "Coolify: A service ({$this->name}) has been stopped on {$this->server->name}";
$message = "Coolify: A resource has been stopped unexpectedly on {$this->server->name}";
return $message;
}
public function toTelegram(): array
{
$message = "Coolify: A service ($this->name} has been stopped on {$this->server->name}";
$message = "Coolify: A resource has been stopped unexpectedly on {$this->server->name}";
$payload = [
"message" => $message,
];

View File

@ -31,7 +31,7 @@ public function toResponse($request)
{
// First user (root) will be redirected to /settings instead of / on registration.
if ($request->user()->currentTeam->id === 0) {
return redirect()->route('settings.configuration');
return redirect()->route('settings.index');
}
return redirect(RouteServiceProvider::HOME);
}

View File

@ -226,8 +226,8 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if (is_null($port) && !is_null($onlyPort)) {
$port = $onlyPort;
}
$http_label = "{$uuid}-{$loop}-http";
$https_label = "{$uuid}-{$loop}-https";
$http_label = "http-{$loop}-{$uuid}";
$https_label = "https-{$loop}-{$uuid}";
if ($schema === 'https') {
// Set labels for https
@ -249,6 +249,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
// Set labels for http (redirect to https)
$labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
if ($port) {
$labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
}
if ($is_force_https_enabled) {
$labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https");
}
@ -258,27 +262,26 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
$labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
if ($port) {
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
$labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
}
if ($path !== '/') {
$labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix");
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
}
}
} catch(\Throwable $e) {
} catch (\Throwable $e) {
continue;
}
}
return $labels;
return $labels->sort();
}
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
{
$ports = $application->settings->is_static ? [80] : $application->ports_exposes_array;
$onlyPort = null;
if (count($ports) === 1) {
if (count($ports) > 0) {
$onlyPort = $ports[0];
}
$pull_request_id = data_get($preview, 'pull_request_id', 0);

View File

@ -106,22 +106,6 @@ function generate_default_proxy_configuration(Server $server)
// Global Middlewares
"traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https",
"traefik.http.middlewares.gzip.compress=true",
// https WWW to non-WWW
"traefik.http.middlewares.https-www-to-non-www.redirectregex.regex=^https?://www\\.(.+)",
"traefik.http.middlewares.https-www-to-non-www.redirectregex.replacement=https://\$1",
"traefik.http.middlewares.https-www-to-non-www.redirectregex.permanent=true",
// https Non-WWW to WWW
"traefik.http.middlewares.https-non-www-to-www.redirectregex.regex=^https?://(?:www\\.)?(.+)",
"traefik.http.middlewares.https-non-www-to-www.redirectregex.replacement=https://www.\$\${1}",
"traefik.http.middlewares.https-non-www-to-www.redirectregex.permanent=true",
// http www to non-WWW
"traefik.http.middlewares.http-www-to-non-www.redirectregex.regex=^http://www\\.(.+)",
"traefik.http.middlewares.http-www-to-non-www.redirectregex.replacement=http://\$1",
"traefik.http.middlewares.http-www-to-non-www.redirectregex.permanent=true",
// http Non-WWW to WWW
"traefik.http.middlewares.http-non-www-to-www.redirectregex.regex=^http://(?:www\\.)?(.+)",
"traefik.http.middlewares.http-non-www-to-www.redirectregex.replacement=http://www.\$\${1}",
"traefik.http.middlewares.http-non-www-to-www.redirectregex.permanent=true",
];
$config = [
"version" => "3.8",
@ -171,7 +155,7 @@ function generate_default_proxy_configuration(Server $server)
],
];
if (isDev()) {
$config['services']['traefik']['command'][] = "--log.level=debug";
// $config['services']['traefik']['command'][] = "--log.level=debug";
$config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
$config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
}

View File

@ -408,7 +408,7 @@ function generateFqdn(Server $server, string $random)
}
function sslip(Server $server)
{
if (isDev()) {
if (isDev() && $server->id === 0) {
return "http://127.0.0.1.sslip.io";
}
if ($server->ip === 'host.docker.internal') {

View File

@ -65,7 +65,7 @@
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 300,
'retry_after' => 3600,
'block_for' => null,
'after_commit' => true,
],

View File

@ -7,7 +7,7 @@
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.183',
'release' => '4.0.0-beta.184',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.183';
return '4.0.0-beta.184';

View File

@ -1,3 +0,0 @@
<x-layout-simple>
<livewire:force-password-reset />
</x-layout-simple>

View File

@ -13,8 +13,8 @@
href="{{ route('project.application.logs', $parameters) }}">
<button>Logs</button>
</a>
<a class="{{ request()->routeIs('project.application.deployments') ? 'text-white' : '' }}"
href="{{ route('project.application.deployments', $parameters) }}">
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'text-white' : '' }}"
href="{{ route('project.application.deployment.index', $parameters) }}">
<button>Deployments</button>
</a>
<x-applications.links :application="$application" />

View File

@ -16,8 +16,8 @@
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
<a class="{{ request()->routeIs('project.database.backups.all') ? 'text-white' : '' }}"
href="{{ route('project.database.backups.all', $parameters) }}">
<a class="{{ request()->routeIs('project.database.backup.index') ? 'text-white' : '' }}"
href="{{ route('project.database.backup.index', $parameters) }}">
<button>Backups</button>
</a>
@endif

View File

@ -14,7 +14,7 @@
clip-rule="evenodd"></path>
</svg>
<a class="text-xs truncate lg:text-sm"
href="{{ route('project.resources', ['environment_name' => $this->parameters['environment_name'], 'project_uuid' => $this->parameters['project_uuid']]) }}">{{ $this->parameters['environment_name'] }}</a>
href="{{ route('project.resource.index', ['environment_name' => $this->parameters['environment_name'], 'project_uuid' => $this->parameters['project_uuid']]) }}">{{ $this->parameters['environment_name'] }}</a>
</div>
</li>
<li>

View File

@ -2,8 +2,8 @@
<h1>Settings</h1>
<div class="subtitle">Instance wide settings for Coolify.</div>
<nav class="navbar-main">
<a class="{{ request()->routeIs('settings.configuration') ? 'text-white' : '' }}"
href="{{ route('settings.configuration') }}">
<a class="{{ request()->routeIs('settings.index') ? 'text-white' : '' }}"
href="{{ route('settings.index') }}">
<button>Configuration</button>
</a>
@if (isCloud())

View File

@ -17,15 +17,15 @@ class="text-warning">{{ session('currentTeam.name') }}</span></span>
<a class="{{ request()->routeIs('team.index') ? 'text-white' : '' }}" href="{{ route('team.index') }}">
<button>General</button>
</a>
<a class="{{ request()->routeIs('team.members') ? 'text-white' : '' }}" href="{{ route('team.members') }}">
<a class="{{ request()->routeIs('team.member.index') ? 'text-white' : '' }}" href="{{ route('team.member.index') }}">
<button>Members</button>
</a>
<a class="{{ request()->routeIs('team.storages.all') ? 'text-white' : '' }}"
href="{{ route('team.storages.all') }}">
<a class="{{ request()->routeIs('team.storage.index') ? 'text-white' : '' }}"
href="{{ route('team.storage.index') }}">
<button>S3 Storages</button>
</a>
<a class="{{ request()->routeIs('team.notifications') ? 'text-white' : '' }}"
href="{{ route('team.notifications') }}">
<a class="{{ request()->routeIs('team.notification.index') ? 'text-white' : '' }}"
href="{{ route('team.notification.index') }}">
<button>Notifications</button>
</a>
<div class="flex-1"></div>

View File

@ -1,11 +1,10 @@
<x-emails.layout>
@if ($pull_request_id === 0)
Failed to deploy a new version of {{ $name }} at [{{ $fqdn }}]({{ $fqdn }}) .
@else
Failed to deploy a pull request #{{ $pull_request_id }} of {{ $name }} at
[{{ $fqdn }}]({{ $fqdn }}).
@endif
[View Deployment Logs]({{ $deployment_url }})
@if ($pull_request_id === 0)
Failed to deploy a new version of {{ $name }} at [{{ $fqdn }}]({{ $fqdn }}) .
@else
Failed to deploy a pull request #{{ $pull_request_id }} of {{ $name }} at
[{{ $fqdn }}]({{ $fqdn }}).
@endif
[View Deployment Logs]({{ $deployment_url }})
</x-emails.layout>

View File

@ -1,11 +1,11 @@
<x-emails.layout>
@if ($pull_request_id === 0)
A new version of {{ $name }} is available at [{{ $fqdn }}]({{ $fqdn }}) .
@else
Pull request #{{ $pull_request_id }} of {{ $name }} deployed successfully
[{{ $fqdn }}]({{ $fqdn }}).
@endif
@if ($pull_request_id === 0)
A new version of {{ $name }} is available at [{{ $fqdn }}]({{ $fqdn }}) .
@else
Pull request #{{ $pull_request_id }} of {{ $name }} deployed successfully
[{{ $fqdn }}]({{ $fqdn }}).
@endif
[View Deployment Logs]({{ $deployment_url }})
[View Deployment Logs]({{ $deployment_url }})
</x-emails.layout>

View File

@ -1,9 +1,7 @@
<x-emails.layout>
{{ $name }} has been stopped.
{{ $name }} has been stopped.
If it was your intention to stop this application, you can ignore this email.
If not, [check what is going on]({{ $application_url }}).
If it was your intention to stop this application, you can ignore this email.
If not, [check what is going on]({{ $application_url }}).
</x-emails.layout>

View File

@ -1,8 +1,7 @@
<x-emails.layout>
Database backup for {{ $name }} with frequency of {{ $frequency }} was FAILED.
Database backup for {{ $name }} with frequency of {{ $frequency }} was FAILED.
### Reason
{{ $output }}
### Reason
{{ $output }}
</x-emails.layout>

View File

@ -1,3 +1,3 @@
<x-emails.layout>
Database backup for {{ $name }} with frequency of {{ $frequency }} was successful.
Database backup for {{ $name }} with frequency of {{ $frequency }} was successful.
</x-emails.layout>

View File

@ -1,9 +1,7 @@
<x-emails.layout>
We would like to inform you that a {{ config('constants.limits.trial_period') }} days of trial has been added to all
subscription plans.
We would like to inform you that a {{ config('constants.limits.trial_period') }} days of trial has been added to all subscription plans.
You can try out Coolify, without payment information for free. If you like it, you can upgrade to a paid plan at any
time.
You can try out Coolify, without payment information for free. If you like it, you can upgrade to a paid plan at any time.
[Click here](https://app.coolify.io/subscription) to start your trial.
[Click here](https://app.coolify.io/subscription) to start your trial.
</x-emails.layout>

View File

@ -1,13 +1,9 @@
<x-emails.layout>
A resource ({{ $containerName }}) has been restarted automatically on {{ $serverName }}, because it was stopped unexpectedly.
A service ({{ $containerName }}) has been restarted automatically on {{ $serverName }}, because it was stopped
unexpectedly.
@if ($containerName === 'coolify-proxy')
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to
Custom(None).
@endif
@if ($containerName === 'coolify-proxy')
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None).
@endif
</x-emails.layout>

View File

@ -1,9 +1,7 @@
<x-emails.layout>
A resource ({{ $containerName }}) has been stopped unexpectedly on {{ $serverName }}.
A service ({{ $containerName }}) has been stopped unexpectedly on {{ $serverName }}.
@if ($url)
Please check what is going on [here]({{ $url }}).
@endif
@if ($url)
Please check what is going on [here]({{ $url }}).
@endif
</x-emails.layout>

View File

@ -1,3 +1,3 @@
<x-emails.layout>
Verify your email [here]({{ $url }}).
Verify your email [here]({{ $url }}).
</x-emails.layout>

View File

@ -1,10 +1,7 @@
<x-emails.layout>
Your server ({{ $name }}) has high disk usage ({{ $disk_usage }}% used). Threshold is {{ $threshold }}%.
Your server ({{ $name }}) has high disk usage ({{ $disk_usage }}% used). Threshold is
{{ $threshold }}%.
Please cleanup your disk to prevent data-loss. Here are some [tips](https://coolify.io/docs/automated-cleanup).
(You can change the threshold in the Server Settings menu.)
Please cleanup your disk to prevent data-loss. Here are some [tips](https://coolify.io/docs/automated-cleanup).
(You can change the threshold in the Server Settings menu.)
</x-emails.layout>

View File

@ -1,10 +1,9 @@
<x-emails.layout>
You have been invited to "{{ $team }}" on "{{ config('app.name') }}".
You have been invited to "{{ $team }}" on "{{ config('app.name') }}".
Please [click here]({{ $invitation_link }}) to accept the invitation.
Please [click here]({{ $invitation_link }}) to accept the invitation.
If you have any questions, please contact the team owner.<br><br>
If you have any questions, please contact the team owner.<br><br>
If it was not you who requested this invitation, please ignore this email.
If it was not you who requested this invitation, please ignore this email.
</x-emails.layout>

View File

@ -1,7 +1,7 @@
<x-emails.layout>
A password reset has been requested for this email address.
A password reset has been requested for this email address.
Click [here]({{ $url }}) to reset your password.
Click [here]({{ $url }}) to reset your password.
This link will expire in {{ $count }} minutes.
This link will expire in {{ $count }} minutes.
</x-emails.layout>

View File

@ -1,6 +1,5 @@
<x-emails.layout>
Connection could not be establised with one of your S3 Storage ({{ $name }}). Please fix it
[here]({{ $url }}).
Connection could not be establised with one of your S3 Storage ({{ $name }}). Please fix it [here]({{ $url }}).
{{ $reason }}
{{ $reason }}
</x-emails.layout>

View File

@ -1,12 +1,9 @@
<x-emails.layout>
Coolify cannot connect to your server ({{ $name }}). Please check your server and make sure it is running.
Coolify cannot connect to your server ({{ $name }}). Please check your server and make sure it is running.
All automations & integrations are turned off!
All automations & integrations are turned off!
IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on
all automations & integrations.
If you have any questions, please contact us.
IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn onall automations & integrations.
If you have any questions, please contact us.
</x-emails.layout>

View File

@ -1,6 +1,3 @@
<x-emails.layout>
Your server ({{ $name }}) was offline for a while, but it is back online now. All automations & integrations
are turned on again.
Your server ({{ $name }}) was offline for a while, but it is back online now. All automations & integrationsare turned on again.
</x-emails.layout>

View File

@ -1,6 +1,5 @@
<x-emails.layout>
Your last invoice has failed to be paid for Coolify Cloud.
Please update payment details [here]({{ $stripeCustomerPortal }}).
Your last invoice has failed to be paid for Coolify Cloud.
Please update payment details [here]({{ $stripeCustomerPortal }}).
</x-emails.layout>

View File

@ -1,3 +1,3 @@
<x-emails.layout>
If you are seeing this, it means that your Email settings are correct.
If you are seeing this, it means that your Email settings are correct.
</x-emails.layout>

View File

@ -1,8 +1,5 @@
<x-emails.layout>
Your trial ended. All automations and integrations are disabled for all of your servers.
Your trial ended. All automations and integrations are disabled for all of your servers.
Please update payment details [here]({{ $stripeCustomerPortal }}) or in [Coolify Cloud](https://app.coolify.io) to
continue using our services.
Please update payment details [here]({{ $stripeCustomerPortal }}) or in [Coolify Cloud](https://app.coolify.io) to continue using our services.
</x-emails.layout>

View File

@ -1,8 +1,5 @@
<x-emails.layout>
Your trial ends soon. Please update payment details [here]({{ $stripeCustomerPortal }}),
Your trial ends soon. Please update payment details [here]({{ $stripeCustomerPortal }}),
Your servers & deployed resources will be untouched, but you won't be able to deploy new resources and lost all
automations and integrations.
Your servers & deployed resources will be untouched, but you won't be able to deploy new resources and lost all automations and integrations.
</x-emails.layout>

View File

@ -1,5 +1,3 @@
<x-emails.layout>
<br><br>
If you do not like to receive these emails, you can unsubscribe [here]({{ $unsubscribeUrl }}).
If you do not like to receive these emails, you can unsubscribe [here]({{ $unsubscribeUrl }}).
</x-emails.layout>

View File

@ -1,10 +1,7 @@
<x-emails.layout>
Someone added this email to the Coolify Cloud's waitlist. [Click here]({{ $confirmation_url }}) to confirm!
Someone added this email to the Coolify Cloud's waitlist. [Click here]({{ $confirmation_url }}) to confirm!
The link will expire in {{ config('constants.waitlist.expiration') }} minutes.
You have no idea what [Coolify Cloud](https://coolify.io) is or this waitlist? [Click here]({{ $cancel_url }}) to
remove you from the waitlist.
The link will expire in {{ config('constants.waitlist.expiration') }} minutes.
You have no idea what [Coolify Cloud](https://coolify.io) is or this waitlist? [Click here]({{ $cancel_url }}) to remove you from the waitlist.
</x-emails.layout>

View File

@ -1,3 +1,3 @@
<x-emails.layout>
You have been invited to join the Coolify Cloud: [Get Started]({{ $loginLink }})
You have been invited to join the Coolify Cloud: [Get Started]({{ $loginLink }})
</x-emails.layout>

View File

@ -1,24 +0,0 @@
<form wire:submit='submit' class="flex flex-col gap-2">
<div>
@if ($settings->is_resale_license_active)
<div class="text-success">License is active</div>
@else
<div class="text-error">License is not active</div>
@endif
</div>
<div class="flex gap-2">
<x-forms.input type="password" id="settings.resale_license" placeholder="eg: BE558E91-0CC5-4AA2-B1C0-B6403C2988DD"
label="License Key" />
<x-forms.input type="password" id="instance_id" label="Instance Id (do not change this)" disabled />
</div>
<div class="flex gap-2">
<x-forms.button type="submit">
Check License
</x-forms.button>
</div>
@if (session()->has('error'))
<div class="text-error">
{!! session('error') !!}
</div>
@endif
</form>

View File

@ -0,0 +1,12 @@
<div>
<h1>Command Center</h1>
<div class="subtitle">Execute commands on your servers without leaving the browser.</div>
@if ($servers->count() > 0)
<livewire:run-command :servers="$servers" />
@else
<div>
<div>No servers found. Without a server, you won't be able to do much.</div>
<x-use-magic-bar link="/server/new" />
</div>
@endif
</div>

View File

@ -30,7 +30,7 @@
<div class="gap-2 border border-transparent cursor-pointer box group">
@if (data_get($project, 'environments.0.name'))
<a class="flex flex-col flex-1 mx-6 hover:no-underline"
href="{{ route('project.resources', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
href="{{ route('project.resource.index', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
<div class="font-bold text-white">{{ $project->name }}</div>
<div class="description">
{{ $project->description }}</div>
@ -45,7 +45,7 @@
@endif
<div class="flex items-center">
<a class="mx-4 rounded group-hover:text-white hover:no-underline"
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
href="{{ route('project.resource.create', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
<span class="font-bold hover:text-warning">+ New Resource</span>
</a>
<a class="mx-4 rounded group-hover:text-white"

View File

@ -1,52 +0,0 @@
<div x-data="{ showPrivateKey: false }">
<x-modal yesOrNo modalId="deletePrivateKey" modalTitle="Delete Private Key">
<x-slot:modalBody>
<p>This private key will be deleted. It is not reversible. <br>Please think again.</p>
</x-slot:modalBody>
</x-modal>
<form class="flex flex-col gap-2" wire:submit='changePrivateKey'>
<div class="flex items-end gap-2">
<h2>Private Key</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($private_key->id > 0)
<x-forms.button isError isModal modalId="deletePrivateKey">
Delete
</x-forms.button>
@endif
</div>
<x-forms.input id="private_key.name" label="Name" required />
<x-forms.input id="private_key.description" label="Description" />
<div>
<div class="flex items-end gap-2 py-2 ">
<div class="pl-1 ">Public Key</div>
</div>
<x-forms.input readonly id="public_key" />
<div class="flex items-end gap-2 py-2 ">
<div class="pl-1 ">Private Key <span class='text-helper'>*</span></div>
<div class="text-xs text-white underline cursor-pointer" x-cloak x-show="!showPrivateKey"
x-on:click="showPrivateKey = true">
Edit
</div>
<div class="text-xs text-white underline cursor-pointer" x-cloak x-show="showPrivateKey"
x-on:click="showPrivateKey = false">
Hide
</div>
</div>
@if ($private_key->is_git_related)
<div class="w-48">
<x-forms.checkbox id="private_key.is_git_related" disabled label="Is used by a Git App?" />
</div>
@endif
<div x-cloak x-show="!showPrivateKey">
<x-forms.input allowToPeak="false" type="password" rows="10" id="private_key.private_key" required
disabled />
</div>
<div x-cloak x-show="showPrivateKey">
<x-forms.textarea rows="10" id="private_key.private_key" required />
</div>
</div>
</form>
</div>

View File

@ -1,12 +0,0 @@
<div>
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2">
<h2>General</h2>
<x-forms.button type="submit" label="Save">Save</x-forms.button>
</div>
<div class="flex gap-2">
<x-forms.input id="name" label="Name" required />
<x-forms.input id="email" label="Email" readonly />
</div>
</form>
</div>

View File

@ -0,0 +1,92 @@
<div>
<h1>Profile</h1>
<div class="subtitle ">Your user profile settings.</div>
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2">
<h2>General</h2>
<x-forms.button type="submit" label="Save">Save</x-forms.button>
</div>
<div class="flex gap-2">
<x-forms.input id="name" label="Name" required />
<x-forms.input id="email" label="Email" readonly />
</div>
</form>
<h2 class="py-4">Subscription</h2>
<a href="{{ route('team.index') }}">Check in Team Settings</a>
<h2 class="py-4">Two-factor Authentication</h2>
@if (session('status') == 'two-factor-authentication-enabled')
<div class="mb-4 font-medium">
Please finish configuring two factor authentication below. Read the QR code or enter the secret key
manually.
</div>
<div class="flex flex-col gap-2">
<form action="/user/confirmed-two-factor-authentication" method="POST" class="flex items-end gap-2">
@csrf
<x-forms.input type="number" id="code" label="One-time code" required />
<x-forms.button type="submit">Validate 2FA</x-forms.button>
</form>
<div>
<div>{!! request()->user()->twoFactorQrCodeSvg() !!}</div>
<div x-data="{ showCode: false }" class="py-2">
<template x-if="showCode">
<div class="py-2 ">{!! decrypt(request()->user()->two_factor_secret) !!}</div>
</template>
<x-forms.button x-on:click="showCode = !showCode">Show secret key to manually
enter</x-forms.button>
</div>
</div>
</div>
@elseif(session('status') == 'two-factor-authentication-confirmed')
<div class="mb-4 ">
Two factor authentication confirmed and enabled successfully.
</div>
<div>
<div class="pb-6 ">Here are the recovery codes for your account. Please store them in a secure
location.
</div>
<div class="text-white">
@foreach (request()->user()->recoveryCodes() as $code)
<div>{{ $code }}</div>
@endforeach
</div>
</div>
@else
@if (request()->user()->two_factor_confirmed_at)
<div class="pb-4 "> Two factor authentication is <span class="text-helper">enabled</span>.</div>
<div class="flex gap-2">
<form action="/user/two-factor-authentication" method="POST">
@csrf
@method ('DELETE')
<x-forms.button type="submit">Disable</x-forms.button>
</form>
<form action="/user/two-factor-recovery-codes" method="POST">
@csrf
<x-forms.button type="submit">Regenerate Recovery Codes</x-forms.button>
</form>
</div>
@if (session('status') == 'recovery-codes-generated')
<div>
<div class="py-6 ">Here are the recovery codes for your account. Please store them in a
secure
location.
</div>
<div class="text-white">
@foreach (request()->user()->recoveryCodes() as $code)
<div>{{ $code }}</div>
@endforeach
</div>
</div>
@endif
@else
<form action="/user/two-factor-authentication" method="POST">
@csrf
<x-forms.button type="submit">Configure 2FA</x-forms.button>
</form>
@endif
@endif
@if (session()->has('errors'))
<div class="text-error">
Something went wrong. Please try again.
</div>
@endif
</div>

Some files were not shown because too many files have changed in this diff Show More