v4.0.0-beta.420 (#6008)
* chore(version): update coolify-realtime to version 1.0.9 in docker-compose and versions files * feat(migration): add is_sentinel_enabled column to server_settings with default true * fix(migration): update default value handling for is_sentinel_enabled column in server_settings * feat(seeder): dispatch StartProxy action for each server in ProductionSeeder * feat(seeder): add CheckAndStartSentinelJob dispatch for each server in ProductionSeeder * fix(seeder): conditionally dispatch CheckAndStartSentinelJob based on server's sentinel status * feat(seeder): conditionally dispatch StartProxy action based on proxy check result * refactor(ui): terminal * refactor(ui): remove terminal header from execute-container-command view * refactor(ui): remove unnecessary padding from deployment, backup, and logs sections * fix(service): disable healthcheck logging for Gotenberg (#6005) * fix(service): Joplin volume name (#5930) * chore(version): update coolify version to 4.0.0-beta.420 and nightly version to 4.0.0-beta.421 * fix(server): update sentinelUpdatedAt assignment to use server's sentinel_updated_at property * feat(service): update Changedetection template (#5937) * chore(service): changedetection remove unused code * fix(service): audiobookshelf healthcheck command (#5993) * refactor(service): update Hoarder to their new name karakeep (#5964) * fix(service): downgrade Evolution API phone version (#5977) * feat(service): add Miniflux service (#5843) * refactor(service): karakeep naming and formatting * refactor(service): improve miniflux - improve DB url - add depends_on - formatting, naming & order * feat(service): add Pingvin Share service (#5969) * fix(service): pingvinshare-with-clamav - add platform to make clamav work - formatting * feat(auth): Add Discord OAuth Provider (#5552) * feat(auth): Add Clerk OAuth Provider (#5553) * feat(auth): add Zitadel OAuth Provider (#5490) * Update composer.lock * fix(ssh): scp requires square brackets for ipv6 (#6001) * refactor(core): rename API rate limit ENV * refactor(ui): simplify container selection form in execute-container-command view * chore(service): Update Evolution API image to the official one (#6031) * chore(versions): bump coolify versions to v4.0.0-beta.420 and v4.0.0-beta.421 * fix(github): changing github app breaks the webhook. it does not anymore * feat(service): enhance service status handling and UI updates * fix(parser): improve FQDN generation and update environment variable handling * fix(ui): enhance status refresh buttons with loading indicators * fix(ui): update confirmation button text for stopping database and service * fix(routes): update middleware for deploy route to use 'api.ability:deploy' * fix(ui): refine API token creation form and update helper text for clarity * fix(ui): adjust layout of deployments section for improved alignment * chore(dependencies): update composer dependencies to latest versions including resend-laravel to ^0.19.0 and aws-sdk-php to 3.347.0 * refactor(email): streamline SMTP and resend settings logic for improved clarity * fix(ui): adjust project grid layout and refine server border styling for better visibility * fix(ui): update border styling for consistency across components and enhance loading indicators * feat(cleanup): add functionality to delete teams with no members or servers in CleanupStuckedResources command * refactor(invitation): rename methods for consistency and enhance invitation deletion logic * refactor(user): streamline user deletion process and enhance team management logic * fix(ui): add padding to section headers in settings views for improved spacing * fix(ui): reduce gap between input fields in email settings for better alignment * fix(docker): conditionally enable gzip compression in Traefik labels based on configuration * fix(parser): enable gzip compression conditionally for Pocketbase images and streamline service creation logic * fix(ui): update padding for trademarks policy and enhance spacing in advanced settings section * feat(ui): add heart icon and enhance popup messaging for sponsorship support * feat(settings): add sponsorship popup toggle and corresponding database migration * fix(ui): correct closing tag for sponsorship link in layout popups * fix(ui): refine wording in sponsorship donation prompt in layout popups * fix(ui): update navbar icon color and enhance popup layout for sponsorship support * Update resources/views/livewire/project/shared/health-checks.blade.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update app/Livewire/Subscription/Index.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(ui): add target="_blank" to sponsorship links in layout popups for improved user experience * fix(models): refine comment wording in User model for clarity on user deletion criteria * Update app/Providers/RouteServiceProvider.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(models): improve user deletion logic in User model to handle team member roles and prevent deletion if user is alone in root team * fix(ui): update wording in sponsorship prompt for clarity and engagement --------- Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Co-authored-by: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Co-authored-by: Carsten <BanditsBacon@users.noreply.github.com> Co-authored-by: Alberto Rizzi <48057685+albertorizzi@users.noreply.github.com> Co-authored-by: Jonas Klesen <deklesen@gmail.com> Co-authored-by: Stew Night. <22344601+stewnight@users.noreply.github.com> Co-authored-by: Jeffer Marcelino <jeffersunde72@gmail.com> Co-authored-by: Lucas Eduardo <lucas59356@gmail.com> Co-authored-by: CrazyTim71 <118295691+CrazyTim71@users.noreply.github.com> Co-authored-by: Yassir Elmarissi <yassir.elmarissi@hm.edu> Co-authored-by: Hauke Schnau <hauke@schnau-lilienthal.de> Co-authored-by: Darren Sisson <74752850+djsisson@users.noreply.github.com> Co-authored-by: Alkesh Das <67038642+smad-bro@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -21,7 +21,9 @@ Coolify implements **defense-in-depth security** with multiple layers of protect
|
|||||||
- **Supported Providers**:
|
- **Supported Providers**:
|
||||||
- Google OAuth
|
- Google OAuth
|
||||||
- Microsoft Azure AD
|
- Microsoft Azure AD
|
||||||
|
- Clerk
|
||||||
- Authentik
|
- Authentik
|
||||||
|
- Discord
|
||||||
- GitHub (via GitHub Apps)
|
- GitHub (via GitHub Apps)
|
||||||
- GitLab
|
- GitLab
|
||||||
|
|
||||||
|
@@ -90,7 +90,7 @@ alwaysApply: false
|
|||||||
- **Purpose**: OAuth provider integration
|
- **Purpose**: OAuth provider integration
|
||||||
- **Providers**:
|
- **Providers**:
|
||||||
- GitHub, GitLab, Google
|
- GitHub, GitLab, Google
|
||||||
- Microsoft Azure, Authentik
|
- Microsoft Azure, Authentik, Discord, Clerk
|
||||||
- Custom OAuth implementations
|
- Custom OAuth implementations
|
||||||
|
|
||||||
## Background Processing
|
## Background Processing
|
||||||
|
@@ -41,6 +41,6 @@ class StartService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return remote_process($commands, $service->server, type_uuid: $service->uuid);
|
return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ use App\Models\StandaloneMongodb;
|
|||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
|
use App\Models\Team;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class CleanupStuckedResources extends Command
|
class CleanupStuckedResources extends Command
|
||||||
@@ -36,6 +37,12 @@ class CleanupStuckedResources extends Command
|
|||||||
private function cleanup_stucked_resources()
|
private function cleanup_stucked_resources()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$teams = Team::all()->filter(function ($team) {
|
||||||
|
return $team->members()->count() === 0 && $team->servers()->count() === 0;
|
||||||
|
});
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
$team->delete();
|
||||||
|
}
|
||||||
$servers = Server::all()->filter(function ($server) {
|
$servers = Server::all()->filter(function ($server) {
|
||||||
return $server->isFunctional();
|
return $server->isFunctional();
|
||||||
});
|
});
|
||||||
|
@@ -103,7 +103,11 @@ class SshMultiplexingHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true);
|
$scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true);
|
||||||
|
if ($server->isIpv6()) {
|
||||||
|
$scp_command .= "{$source} {$server->user}@[{$server->ip}]:{$dest}";
|
||||||
|
} else {
|
||||||
$scp_command .= "{$source} {$server->user}@{$server->ip}:{$dest}";
|
$scp_command .= "{$source} {$server->user}@{$server->ip}:{$dest}";
|
||||||
|
}
|
||||||
|
|
||||||
return $scp_command;
|
return $scp_command;
|
||||||
}
|
}
|
||||||
|
@@ -144,7 +144,7 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revoke_invitation()
|
public function revokeInvitation()
|
||||||
{
|
{
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||||
|
@@ -1381,8 +1381,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if (is_object($this->source) && $this->source->getMorphClass() === \App\Models\GithubApp::class && $this->source->is_public === false) {
|
if (is_object($this->source) && $this->source->getMorphClass() === \App\Models\GithubApp::class && $this->source->is_public === false) {
|
||||||
$repository = githubApi($this->source, "repos/{$this->customRepository}");
|
$repository = githubApi($this->source, "repos/{$this->customRepository}");
|
||||||
$data = data_get($repository, 'data');
|
$data = data_get($repository, 'data');
|
||||||
if (isset($data->id)) {
|
$repository_project_id = data_get($data, 'id');
|
||||||
$repository_project_id = $data->id;
|
if (isset($repository_project_id)) {
|
||||||
if (blank($this->application->repository_project_id) || $this->application->repository_project_id !== $repository_project_id) {
|
if (blank($this->application->repository_project_id) || $this->application->repository_project_id !== $repository_project_id) {
|
||||||
$this->application->repository_project_id = $repository_project_id;
|
$this->application->repository_project_id = $repository_project_id;
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
|
@@ -254,10 +254,9 @@ class Email extends Component
|
|||||||
'smtpEncryption.required' => 'Encryption type is required.',
|
'smtpEncryption.required' => 'Encryption type is required.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->settings->resend_enabled = false;
|
if ($this->smtpEnabled) {
|
||||||
$this->settings->use_instance_email_settings = false;
|
$this->settings->resend_enabled = $this->resendEnabled = false;
|
||||||
$this->resendEnabled = false;
|
}
|
||||||
$this->useInstanceEmailSettings = false;
|
|
||||||
|
|
||||||
$this->settings->smtp_enabled = $this->smtpEnabled;
|
$this->settings->smtp_enabled = $this->smtpEnabled;
|
||||||
$this->settings->smtp_from_address = $this->smtpFromAddress;
|
$this->settings->smtp_from_address = $this->smtpFromAddress;
|
||||||
@@ -293,11 +292,9 @@ class Email extends Component
|
|||||||
'smtpFromAddress.email' => 'Please enter a valid email address.',
|
'smtpFromAddress.email' => 'Please enter a valid email address.',
|
||||||
'smtpFromName.required' => 'From Name is required.',
|
'smtpFromName.required' => 'From Name is required.',
|
||||||
]);
|
]);
|
||||||
|
if ($this->resendEnabled) {
|
||||||
$this->settings->smtp_enabled = false;
|
$this->settings->smtp_enabled = $this->smtpEnabled = false;
|
||||||
$this->settings->use_instance_email_settings = false;
|
}
|
||||||
$this->smtpEnabled = false;
|
|
||||||
$this->useInstanceEmailSettings = false;
|
|
||||||
|
|
||||||
$this->settings->resend_enabled = $this->resendEnabled;
|
$this->settings->resend_enabled = $this->resendEnabled;
|
||||||
$this->settings->resend_api_key = $this->resendApiKey;
|
$this->settings->resend_api_key = $this->resendApiKey;
|
||||||
|
@@ -111,8 +111,19 @@ class Source extends Component
|
|||||||
$this->application->update([
|
$this->application->update([
|
||||||
'source_id' => $sourceId,
|
'source_id' => $sourceId,
|
||||||
'source_type' => $sourceType,
|
'source_type' => $sourceType,
|
||||||
'repository_project_id' => null,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
['repository' => $customRepository] = $this->application->customRepository();
|
||||||
|
$repository = githubApi($this->application->source, "repos/{$customRepository}");
|
||||||
|
$data = data_get($repository, 'data');
|
||||||
|
$repository_project_id = data_get($data, 'id');
|
||||||
|
if (isset($repository_project_id)) {
|
||||||
|
if ($this->application->repository_project_id !== $repository_project_id) {
|
||||||
|
$this->application->repository_project_id = $repository_project_id;
|
||||||
|
$this->application->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->getSources();
|
$this->getSources();
|
||||||
$this->dispatch('success', 'Source updated!');
|
$this->dispatch('success', 'Source updated!');
|
||||||
|
@@ -6,7 +6,6 @@ use App\Actions\Docker\GetContainersStatus;
|
|||||||
use App\Actions\Service\StartService;
|
use App\Actions\Service\StartService;
|
||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\StopService;
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
use App\Events\ServiceStatusChanged;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -96,7 +95,7 @@ class Heading extends Component
|
|||||||
public function start()
|
public function start()
|
||||||
{
|
{
|
||||||
$activity = StartService::run($this->service, pullLatestImages: true);
|
$activity = StartService::run($this->service, pullLatestImages: true);
|
||||||
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function forceDeploy()
|
public function forceDeploy()
|
||||||
@@ -112,7 +111,7 @@ class Heading extends Component
|
|||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
|
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
|
||||||
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->dispatch('error', $e->getMessage());
|
$this->dispatch('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
@@ -136,7 +135,7 @@ class Heading extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$activity = StartService::run($this->service, stopBeforeStart: true);
|
$activity = StartService::run($this->service, stopBeforeStart: true);
|
||||||
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pullAndRestartEvent()
|
public function pullAndRestartEvent()
|
||||||
@@ -148,7 +147,7 @@ class Heading extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
|
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
|
||||||
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@@ -19,7 +19,15 @@ class Proxy extends Component
|
|||||||
|
|
||||||
public ?string $redirect_url = null;
|
public ?string $redirect_url = null;
|
||||||
|
|
||||||
protected $listeners = ['saveConfiguration' => 'submit'];
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'saveConfiguration' => 'submit',
|
||||||
|
"echo-private:team.{$teamId},ProxyStatusChangedUI" => '$refresh',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'server.settings.generate_exact_labels' => 'required|boolean',
|
'server.settings.generate_exact_labels' => 'required|boolean',
|
||||||
|
@@ -69,6 +69,7 @@ class Patches extends Component
|
|||||||
{
|
{
|
||||||
if (! $this->packageManager || ! $this->osId) {
|
if (! $this->packageManager || ! $this->osId) {
|
||||||
$this->dispatch('error', message: 'Run “Check for updates” first.');
|
$this->dispatch('error', message: 'Run “Check for updates” first.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -176,7 +176,7 @@ class Show extends Component
|
|||||||
$this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
|
$this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
|
||||||
$this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled;
|
$this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled;
|
||||||
$this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled;
|
$this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled;
|
||||||
$this->sentinelUpdatedAt = $this->server->settings->updated_at;
|
$this->sentinelUpdatedAt = $this->server->sentinel_updated_at;
|
||||||
$this->serverTimezone = $this->server->settings->server_timezone;
|
$this->serverTimezone = $this->server->settings->server_timezone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -68,6 +68,9 @@ class Index extends Component
|
|||||||
#[Validate('boolean')]
|
#[Validate('boolean')]
|
||||||
public bool $disable_two_step_confirmation;
|
public bool $disable_two_step_confirmation;
|
||||||
|
|
||||||
|
#[Validate('boolean')]
|
||||||
|
public bool $is_sponsorship_popup_enabled;
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.settings.index');
|
return view('livewire.settings.index');
|
||||||
@@ -96,6 +99,7 @@ class Index extends Component
|
|||||||
$this->update_check_frequency = $this->settings->update_check_frequency;
|
$this->update_check_frequency = $this->settings->update_check_frequency;
|
||||||
$this->instance_timezone = $this->settings->instance_timezone;
|
$this->instance_timezone = $this->settings->instance_timezone;
|
||||||
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
|
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
|
||||||
|
$this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +138,7 @@ class Index extends Component
|
|||||||
$this->settings->update_check_frequency = $this->update_check_frequency;
|
$this->settings->update_check_frequency = $this->update_check_frequency;
|
||||||
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation;
|
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation;
|
||||||
$this->settings->instance_timezone = $this->instance_timezone;
|
$this->settings->instance_timezone = $this->instance_timezone;
|
||||||
|
$this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled;
|
||||||
if ($isSave) {
|
if ($isSave) {
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->dispatch('success', 'Settings updated!');
|
$this->dispatch('success', 'Settings updated!');
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Livewire\Team;
|
namespace App\Livewire\Team;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Team;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
@@ -53,30 +52,12 @@ class AdminView extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function finalizeDeletion(User $user, Team $team)
|
|
||||||
{
|
|
||||||
$servers = $team->servers;
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
$resources = $server->definedResources();
|
|
||||||
foreach ($resources as $resource) {
|
|
||||||
$resource->forceDelete();
|
|
||||||
}
|
|
||||||
$server->forceDelete();
|
|
||||||
}
|
|
||||||
|
|
||||||
$projects = $team->projects;
|
|
||||||
foreach ($projects as $project) {
|
|
||||||
$project->forceDelete();
|
|
||||||
}
|
|
||||||
$team->members()->detach($user->id);
|
|
||||||
$team->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete($id, $password)
|
public function delete($id, $password)
|
||||||
{
|
{
|
||||||
if (! isInstanceAdmin()) {
|
if (! isInstanceAdmin()) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) {
|
if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) {
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
@@ -84,52 +65,22 @@ class AdminView extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! auth()->user()->isInstanceAdmin()) {
|
if (! auth()->user()->isInstanceAdmin()) {
|
||||||
return $this->dispatch('error', 'You are not authorized to delete users');
|
return $this->dispatch('error', 'You are not authorized to delete users');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = User::find($id);
|
$user = User::find($id);
|
||||||
$teams = $user->teams;
|
if (! $user) {
|
||||||
foreach ($teams as $team) {
|
return $this->dispatch('error', 'User not found');
|
||||||
$user_alone_in_team = $team->members->count() === 1;
|
|
||||||
if ($team->id === 0) {
|
|
||||||
if ($user_alone_in_team) {
|
|
||||||
return $this->dispatch('error', 'User is alone in the root team, cannot delete');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($user_alone_in_team) {
|
|
||||||
$this->finalizeDeletion($user, $team);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($user->isOwner()) {
|
|
||||||
$found_other_owner_or_admin = $team->members->filter(function ($member) {
|
|
||||||
return $member->pivot->role === 'owner' || $member->pivot->role === 'admin';
|
|
||||||
})->where('id', '!=', $user->id)->first();
|
|
||||||
|
|
||||||
if ($found_other_owner_or_admin) {
|
|
||||||
$team->members()->detach($user->id);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$found_other_member_who_is_not_owner = $team->members->filter(function ($member) {
|
|
||||||
return $member->pivot->role === 'member';
|
|
||||||
})->first();
|
|
||||||
if ($found_other_member_who_is_not_owner) {
|
|
||||||
$found_other_member_who_is_not_owner->pivot->role = 'owner';
|
|
||||||
$found_other_member_who_is_not_owner->pivot->save();
|
|
||||||
$team->members()->detach($user->id);
|
|
||||||
} else {
|
|
||||||
$this->finalizeDeletion($user, $team);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
try {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$team->members()->detach($user->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$user->delete();
|
$user->delete();
|
||||||
$this->getUsers();
|
$this->getUsers();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->dispatch('error', $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Livewire\Team;
|
namespace App\Livewire\Team;
|
||||||
|
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
|
use App\Models\User;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Invitations extends Component
|
class Invitations extends Component
|
||||||
@@ -14,8 +15,13 @@ class Invitations extends Component
|
|||||||
public function deleteInvitation(int $invitation_id)
|
public function deleteInvitation(int $invitation_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$initiation_found = TeamInvitation::ownedByCurrentTeam()->findOrFail($invitation_id);
|
$invitation = TeamInvitation::ownedByCurrentTeam()->findOrFail($invitation_id);
|
||||||
$initiation_found->delete();
|
$user = User::whereEmail($invitation->email)->first();
|
||||||
|
if (filled($user)) {
|
||||||
|
$user->deleteIfNotVerifiedAndForcePasswordReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
$invitation->delete();
|
||||||
$this->refreshInvitations();
|
$this->refreshInvitations();
|
||||||
$this->dispatch('success', 'Invitation revoked.');
|
$this->dispatch('success', 'Invitation revoked.');
|
||||||
} catch (\Exception) {
|
} catch (\Exception) {
|
||||||
|
@@ -29,15 +29,15 @@ class InviteLink extends Component
|
|||||||
|
|
||||||
public function viaEmail()
|
public function viaEmail()
|
||||||
{
|
{
|
||||||
$this->generate_invite_link(sendEmail: true);
|
$this->generateInviteLink(sendEmail: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function viaLink()
|
public function viaLink()
|
||||||
{
|
{
|
||||||
$this->generate_invite_link(sendEmail: false);
|
$this->generateInviteLink(sendEmail: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_invite_link(bool $sendEmail = false)
|
private function generateInviteLink(bool $sendEmail = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
@@ -27,6 +27,7 @@ class OauthSetting extends Model
|
|||||||
case 'azure':
|
case 'azure':
|
||||||
return filled($this->client_id) && filled($this->client_secret) && filled($this->tenant);
|
return filled($this->client_id) && filled($this->client_secret) && filled($this->tenant);
|
||||||
case 'authentik':
|
case 'authentik':
|
||||||
|
case 'clerk':
|
||||||
return filled($this->client_id) && filled($this->client_secret) && filled($this->base_url);
|
return filled($this->client_id) && filled($this->client_secret) && filled($this->base_url);
|
||||||
default:
|
default:
|
||||||
return filled($this->client_id) && filled($this->client_secret);
|
return filled($this->client_id) && filled($this->client_secret);
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\ProcessStatus;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@@ -9,6 +10,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
use Spatie\Activitylog\Models\Activity;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -116,6 +118,18 @@ class Service extends BaseModel
|
|||||||
return (bool) str($this->status)->contains('exited');
|
return (bool) str($this->status)->contains('exited');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isStarting(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$activity = Activity::where('properties->type_uuid', $this->uuid)->latest()->first();
|
||||||
|
$status = data_get($activity, 'properties.status');
|
||||||
|
|
||||||
|
return $status === ProcessStatus::QUEUED->value || $status === ProcessStatus::IN_PROGRESS->value;
|
||||||
|
} catch (\Throwable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
@@ -159,6 +173,10 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
public function getStatusAttribute()
|
public function getStatusAttribute()
|
||||||
{
|
{
|
||||||
|
if ($this->isStarting()) {
|
||||||
|
return 'starting:unhealthy';
|
||||||
|
}
|
||||||
|
|
||||||
$applications = $this->applications;
|
$applications = $this->applications;
|
||||||
$databases = $this->databases;
|
$databases = $this->databases;
|
||||||
|
|
||||||
|
@@ -33,6 +33,10 @@ class TeamInvitation extends Model
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$this->delete();
|
$this->delete();
|
||||||
|
$user = User::whereEmail($this->email)->first();
|
||||||
|
if (filled($user)) {
|
||||||
|
$user->deleteIfNotVerifiedAndForcePasswordReset();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,93 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
$new_team = Team::create($team);
|
$new_team = Team::create($team);
|
||||||
$user->teams()->attach($new_team, ['role' => 'owner']);
|
$user->teams()->attach($new_team, ['role' => 'owner']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static::deleting(function (User $user) {
|
||||||
|
\DB::transaction(function () use ($user) {
|
||||||
|
$teams = $user->teams;
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
$user_alone_in_team = $team->members->count() === 1;
|
||||||
|
|
||||||
|
// Prevent deletion if user is alone in root team
|
||||||
|
if ($team->id === 0 && $user_alone_in_team) {
|
||||||
|
throw new \Exception('User is alone in the root team, cannot delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user_alone_in_team) {
|
||||||
|
static::finalizeTeamDeletion($user, $team);
|
||||||
|
// Delete any pending team invitations for this user
|
||||||
|
TeamInvitation::whereEmail($user->email)->delete();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the user's role for this team
|
||||||
|
$userRole = $team->members->where('id', $user->id)->first()?->pivot?->role;
|
||||||
|
|
||||||
|
if ($userRole === 'owner') {
|
||||||
|
$found_other_owner_or_admin = $team->members->filter(function ($member) use ($user) {
|
||||||
|
return ($member->pivot->role === 'owner' || $member->pivot->role === 'admin') && $member->id !== $user->id;
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($found_other_owner_or_admin) {
|
||||||
|
$team->members()->detach($user->id);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$found_other_member_who_is_not_owner = $team->members->filter(function ($member) {
|
||||||
|
return $member->pivot->role === 'member';
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($found_other_member_who_is_not_owner) {
|
||||||
|
$found_other_member_who_is_not_owner->pivot->role = 'owner';
|
||||||
|
$found_other_member_who_is_not_owner->pivot->save();
|
||||||
|
$team->members()->detach($user->id);
|
||||||
|
} else {
|
||||||
|
static::finalizeTeamDeletion($user, $team);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$team->members()->detach($user->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize team deletion by cleaning up all associated resources
|
||||||
|
*/
|
||||||
|
private static function finalizeTeamDeletion(User $user, Team $team)
|
||||||
|
{
|
||||||
|
$servers = $team->servers;
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$resources = $server->definedResources();
|
||||||
|
foreach ($resources as $resource) {
|
||||||
|
$resource->forceDelete();
|
||||||
|
}
|
||||||
|
$server->forceDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
$projects = $team->projects;
|
||||||
|
foreach ($projects as $project) {
|
||||||
|
$project->forceDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
$team->members()->detach($user->id);
|
||||||
|
$team->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the user if they are not verified and have a force password reset.
|
||||||
|
* This is used to clean up users that have been invited, did not accept the invitation (and did not verify their email and have a force password reset).
|
||||||
|
*/
|
||||||
|
public function deleteIfNotVerifiedAndForcePasswordReset()
|
||||||
|
{
|
||||||
|
if ($this->hasVerifiedEmail() === false && $this->force_password_reset === true) {
|
||||||
|
$this->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function recreate_personal_team()
|
public function recreate_personal_team()
|
||||||
|
@@ -9,9 +9,12 @@ use Illuminate\Foundation\Events\MaintenanceModeEnabled;
|
|||||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
use SocialiteProviders\Authentik\AuthentikExtendSocialite;
|
use SocialiteProviders\Authentik\AuthentikExtendSocialite;
|
||||||
use SocialiteProviders\Azure\AzureExtendSocialite;
|
use SocialiteProviders\Azure\AzureExtendSocialite;
|
||||||
|
use SocialiteProviders\Clerk\ClerkExtendSocialite;
|
||||||
|
use SocialiteProviders\Discord\DiscordExtendSocialite;
|
||||||
use SocialiteProviders\Google\GoogleExtendSocialite;
|
use SocialiteProviders\Google\GoogleExtendSocialite;
|
||||||
use SocialiteProviders\Infomaniak\InfomaniakExtendSocialite;
|
use SocialiteProviders\Infomaniak\InfomaniakExtendSocialite;
|
||||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||||
|
use SocialiteProviders\Zitadel\ZitadelExtendSocialite;
|
||||||
|
|
||||||
class EventServiceProvider extends ServiceProvider
|
class EventServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@@ -25,8 +28,11 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
SocialiteWasCalled::class => [
|
SocialiteWasCalled::class => [
|
||||||
AzureExtendSocialite::class.'@handle',
|
AzureExtendSocialite::class.'@handle',
|
||||||
AuthentikExtendSocialite::class.'@handle',
|
AuthentikExtendSocialite::class.'@handle',
|
||||||
|
ClerkExtendSocialite::class.'@handle',
|
||||||
|
DiscordExtendSocialite::class.'@handle',
|
||||||
GoogleExtendSocialite::class.'@handle',
|
GoogleExtendSocialite::class.'@handle',
|
||||||
InfomaniakExtendSocialite::class.'@handle',
|
InfomaniakExtendSocialite::class.'@handle',
|
||||||
|
ZitadelExtendSocialite::class.'@handle',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip());
|
return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Limit::perMinute(config('api.throttle'))->by($request->user()?->id ?: $request->ip());
|
return Limit::perMinute((int) config('api.rate_limit'))->by($request->user()?->id ?: $request->ip());
|
||||||
});
|
});
|
||||||
RateLimiter::for('5', function (Request $request) {
|
RateLimiter::for('5', function (Request $request) {
|
||||||
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
|
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
|
||||||
|
@@ -359,7 +359,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels->push('traefik.enable=true');
|
$labels->push('traefik.enable=true');
|
||||||
|
if ($is_gzip_enabled) {
|
||||||
$labels->push('traefik.http.middlewares.gzip.compress=true');
|
$labels->push('traefik.http.middlewares.gzip.compress=true');
|
||||||
|
}
|
||||||
$labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https');
|
$labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https');
|
||||||
|
|
||||||
$is_http_basic_auth_enabled = $is_http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
|
$is_http_basic_auth_enabled = $is_http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
|
||||||
|
@@ -2991,12 +2991,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$applicationFound = ServiceApplication::where('name', $serviceName)->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([
|
|
||||||
// 'name' => $applicationFound->name,
|
|
||||||
// 'image' => $applicationFound->image,
|
|
||||||
// 'service_id' => $applicationFound->service_id,
|
|
||||||
// ]);
|
|
||||||
// $applicationFound->delete();
|
|
||||||
} else {
|
} else {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
@@ -3007,15 +3001,23 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$savedService = ServiceApplication::firstOrCreate([
|
$savedService = ServiceApplication::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
'service_id' => $resource->id,
|
'service_id' => $resource->id,
|
||||||
|
], [
|
||||||
|
'is_gzip_enabled' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if image changed
|
// Check if image changed
|
||||||
if ($savedService->image !== $image) {
|
if ($savedService->image !== $image) {
|
||||||
$savedService->image = $image;
|
$savedService->image = $image;
|
||||||
$savedService->save();
|
$savedService->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pocketbase does not need gzip for SSE.
|
||||||
|
if (str($savedService->image)->contains('pocketbase') && $savedService->is_gzip_enabled) {
|
||||||
|
$savedService->is_gzip_enabled = false;
|
||||||
|
$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);
|
||||||
@@ -3060,14 +3062,20 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$port = null;
|
$port = null;
|
||||||
}
|
}
|
||||||
if ($isApplication) {
|
if ($isApplication) {
|
||||||
|
$fqdn = $resource->fqdn;
|
||||||
|
if (blank($resource->fqdn)) {
|
||||||
$fqdn = generateFqdn($server, "$uuid");
|
$fqdn = generateFqdn($server, "$uuid");
|
||||||
|
}
|
||||||
} elseif ($isService) {
|
} elseif ($isService) {
|
||||||
|
$fqdn = $savedService->fqdn;
|
||||||
|
if (blank($savedService->fqdn)) {
|
||||||
if ($fqdnFor) {
|
if ($fqdnFor) {
|
||||||
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
|
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
|
||||||
} else {
|
} else {
|
||||||
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
|
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($value && get_class($value) === \Illuminate\Support\Stringable::class && $value->startsWith('/')) {
|
if ($value && get_class($value) === \Illuminate\Support\Stringable::class && $value->startsWith('/')) {
|
||||||
$path = $value->value();
|
$path = $value->value();
|
||||||
@@ -3090,7 +3098,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (substr_count(str($key)->value(), '_') === 2) {
|
if (substr_count(str($key)->value(), '_') === 2) {
|
||||||
$resource->environment_variables()->firstOrCreate([
|
$resource->environment_variables()->updateOrCreate([
|
||||||
'key' => $key->value(),
|
'key' => $key->value(),
|
||||||
'resourceable_type' => get_class($resource),
|
'resourceable_type' => get_class($resource),
|
||||||
'resourceable_id' => $resource->id,
|
'resourceable_id' => $resource->id,
|
||||||
@@ -3102,7 +3110,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
}
|
}
|
||||||
if (substr_count(str($key)->value(), '_') === 3) {
|
if (substr_count(str($key)->value(), '_') === 3) {
|
||||||
$newKey = str($key)->beforeLast('_');
|
$newKey = str($key)->beforeLast('_');
|
||||||
$resource->environment_variables()->firstOrCreate([
|
$resource->environment_variables()->updateOrCreate([
|
||||||
'key' => $newKey->value(),
|
'key' => $newKey->value(),
|
||||||
'resourceable_type' => get_class($resource),
|
'resourceable_type' => get_class($resource),
|
||||||
'resourceable_id' => $resource->id,
|
'resourceable_id' => $resource->id,
|
||||||
|
@@ -22,15 +22,26 @@ function get_socialite_provider(string $provider)
|
|||||||
return Socialite::driver('azure')->setConfig($azure_config);
|
return Socialite::driver('azure')->setConfig($azure_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($provider == 'authentik') {
|
if ($provider == 'authentik' || $provider == 'clerk') {
|
||||||
$authentik_config = new \SocialiteProviders\Manager\Config(
|
$authentik_clerk_config = new \SocialiteProviders\Manager\Config(
|
||||||
$oauth_setting->client_id,
|
$oauth_setting->client_id,
|
||||||
$oauth_setting->client_secret,
|
$oauth_setting->client_secret,
|
||||||
$oauth_setting->redirect_uri,
|
$oauth_setting->redirect_uri,
|
||||||
['base_url' => $oauth_setting->base_url],
|
['base_url' => $oauth_setting->base_url],
|
||||||
);
|
);
|
||||||
|
|
||||||
return Socialite::driver('authentik')->setConfig($authentik_config);
|
return Socialite::driver($provider)->setConfig($authentik_clerk_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($provider == 'zitadel') {
|
||||||
|
$zitadel_config = new \SocialiteProviders\Manager\Config(
|
||||||
|
$oauth_setting->client_id,
|
||||||
|
$oauth_setting->client_secret,
|
||||||
|
$oauth_setting->redirect_uri,
|
||||||
|
['base_url' => $oauth_setting->base_url],
|
||||||
|
);
|
||||||
|
|
||||||
|
return Socialite::driver('zitadel')->setConfig($zitadel_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($provider == 'google') {
|
if ($provider == 'google') {
|
||||||
@@ -53,6 +64,7 @@ function get_socialite_provider(string $provider)
|
|||||||
|
|
||||||
$provider_class_map = [
|
$provider_class_map = [
|
||||||
'bitbucket' => \Laravel\Socialite\Two\BitbucketProvider::class,
|
'bitbucket' => \Laravel\Socialite\Two\BitbucketProvider::class,
|
||||||
|
'discord' => \SocialiteProviders\Discord\Provider::class,
|
||||||
'github' => \Laravel\Socialite\Two\GithubProvider::class,
|
'github' => \Laravel\Socialite\Two\GithubProvider::class,
|
||||||
'gitlab' => \Laravel\Socialite\Two\GitlabProvider::class,
|
'gitlab' => \Laravel\Socialite\Two\GitlabProvider::class,
|
||||||
'infomaniak' => \SocialiteProviders\Infomaniak\Provider::class,
|
'infomaniak' => \SocialiteProviders\Infomaniak\Provider::class,
|
||||||
|
@@ -36,12 +36,15 @@
|
|||||||
"poliander/cron": "^3.2.1",
|
"poliander/cron": "^3.2.1",
|
||||||
"purplepixie/phpdns": "^2.2",
|
"purplepixie/phpdns": "^2.2",
|
||||||
"pusher/pusher-php-server": "^7.2.7",
|
"pusher/pusher-php-server": "^7.2.7",
|
||||||
"resend/resend-laravel": "^0.17.0",
|
"resend/resend-laravel": "^0.19.0",
|
||||||
"sentry/sentry-laravel": "^4.13",
|
"sentry/sentry-laravel": "^4.13",
|
||||||
"socialiteproviders/authentik": "^5.2",
|
"socialiteproviders/authentik": "^5.2",
|
||||||
|
"socialiteproviders/clerk": "^5.0",
|
||||||
|
"socialiteproviders/discord": "^4.2",
|
||||||
"socialiteproviders/google": "^4.1",
|
"socialiteproviders/google": "^4.1",
|
||||||
"socialiteproviders/infomaniak": "^4.0",
|
"socialiteproviders/infomaniak": "^4.0",
|
||||||
"socialiteproviders/microsoft-azure": "^5.2",
|
"socialiteproviders/microsoft-azure": "^5.2",
|
||||||
|
"socialiteproviders/zitadel": "^4.1",
|
||||||
"spatie/laravel-activitylog": "^4.10.1",
|
"spatie/laravel-activitylog": "^4.10.1",
|
||||||
"spatie/laravel-data": "^4.13.1",
|
"spatie/laravel-data": "^4.13.1",
|
||||||
"spatie/laravel-ray": "^1.39.1",
|
"spatie/laravel-ray": "^1.39.1",
|
||||||
|
585
composer.lock
generated
585
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'throttle' => env('API_THROTTLE', 200),
|
'rate_limit' => env('API_RATE_LIMIT', 200),
|
||||||
];
|
];
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'coolify' => [
|
'coolify' => [
|
||||||
'version' => '4.0.0-beta.419',
|
'version' => '4.0.0-beta.420',
|
||||||
'helper_version' => '1.0.8',
|
'helper_version' => '1.0.8',
|
||||||
'realtime_version' => '1.0.9',
|
'realtime_version' => '1.0.9',
|
||||||
'self_hosted' => env('SELF_HOSTED', true),
|
'self_hosted' => env('SELF_HOSTED', true),
|
||||||
|
@@ -46,6 +46,13 @@ return [
|
|||||||
'redirect' => env('AUTHENTIK_REDIRECT_URI'),
|
'redirect' => env('AUTHENTIK_REDIRECT_URI'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'clerk' => [
|
||||||
|
'client_id' => env('CLERK_CLIENT_ID'),
|
||||||
|
'client_secret' => env('CLERK_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('CLERK_REDIRECT_URI'),
|
||||||
|
'base_url' => env('CLERK_BASE_URL'),
|
||||||
|
],
|
||||||
|
|
||||||
'google' => [
|
'google' => [
|
||||||
'client_id' => env('GOOGLE_CLIENT_ID'),
|
'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||||
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||||
@@ -53,4 +60,11 @@ return [
|
|||||||
'tenant' => env('GOOGLE_TENANT'),
|
'tenant' => env('GOOGLE_TENANT'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'zitadel' => [
|
||||||
|
'client_id' => env('ZITADEL_CLIENT_ID'),
|
||||||
|
'client_secret' => env('ZITADEL_CLIENT_SECRET'),
|
||||||
|
'redirect' => env('ZITADEL_REDIRECT_URI'),
|
||||||
|
'base_url' => env('ZITADEL_BASE_URL'),
|
||||||
|
]
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@@ -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('instance_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_sponsorship_popup_enabled')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('instance_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_sponsorship_popup_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -17,11 +17,14 @@ class OauthSettingSeeder extends Seeder
|
|||||||
$providers = collect([
|
$providers = collect([
|
||||||
'azure',
|
'azure',
|
||||||
'bitbucket',
|
'bitbucket',
|
||||||
|
'clerk',
|
||||||
|
'discord',
|
||||||
'github',
|
'github',
|
||||||
'gitlab',
|
'gitlab',
|
||||||
'google',
|
'google',
|
||||||
'authentik',
|
'authentik',
|
||||||
'infomaniak',
|
'infomaniak',
|
||||||
|
'zitadel',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$isOauthSeeded = OauthSetting::count() > 0;
|
$isOauthSeeded = OauthSetting::count() > 0;
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "تسجيل الدخول باستخدام Authentik",
|
"auth.login.authentik": "تسجيل الدخول باستخدام Authentik",
|
||||||
"auth.login.azure": "تسجيل الدخول باستخدام Microsoft",
|
"auth.login.azure": "تسجيل الدخول باستخدام Microsoft",
|
||||||
"auth.login.bitbucket": "تسجيل الدخول باستخدام Bitbucket",
|
"auth.login.bitbucket": "تسجيل الدخول باستخدام Bitbucket",
|
||||||
|
"auth.login.clerk": "تسجيل الدخول باستخدام Clerk",
|
||||||
|
"auth.login.discord": "تسجيل الدخول باستخدام Discord",
|
||||||
"auth.login.github": "تسجيل الدخول باستخدام GitHub",
|
"auth.login.github": "تسجيل الدخول باستخدام GitHub",
|
||||||
"auth.login.gitlab": "تسجيل الدخول باستخدام Gitlab",
|
"auth.login.gitlab": "تسجيل الدخول باستخدام Gitlab",
|
||||||
"auth.login.google": "تسجيل الدخول باستخدام Google",
|
"auth.login.google": "تسجيل الدخول باستخدام Google",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "Authentik ilə daxil ol",
|
"auth.login.authentik": "Authentik ilə daxil ol",
|
||||||
"auth.login.azure": "Azure ilə daxil ol",
|
"auth.login.azure": "Azure ilə daxil ol",
|
||||||
"auth.login.bitbucket": "Bitbucket ilə daxil ol",
|
"auth.login.bitbucket": "Bitbucket ilə daxil ol",
|
||||||
|
"auth.login.clerk": "Clerk ilə daxil ol",
|
||||||
|
"auth.login.discord": "Discord ilə daxil ol",
|
||||||
"auth.login.github": "Github ilə daxil ol",
|
"auth.login.github": "Github ilə daxil ol",
|
||||||
"auth.login.gitlab": "GitLab ilə daxil ol",
|
"auth.login.gitlab": "GitLab ilə daxil ol",
|
||||||
"auth.login.google": "Google ilə daxil ol",
|
"auth.login.google": "Google ilə daxil ol",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "Přihlásit se",
|
"auth.login": "Přihlásit se",
|
||||||
"auth.login.azure": "Přihlásit se pomocí Microsoftu",
|
"auth.login.azure": "Přihlásit se pomocí Microsoftu",
|
||||||
"auth.login.bitbucket": "Přihlásit se pomocí Bitbucketu",
|
"auth.login.bitbucket": "Přihlásit se pomocí Bitbucketu",
|
||||||
|
"auth.login.clerk": "Přihlásit se pomocí Clerk",
|
||||||
|
"auth.login.discord": "Přihlásit se pomocí Discordu",
|
||||||
"auth.login.github": "Přihlásit se pomocí GitHubu",
|
"auth.login.github": "Přihlásit se pomocí GitHubu",
|
||||||
"auth.login.gitlab": "Přihlásit se pomocí Gitlabu",
|
"auth.login.gitlab": "Přihlásit se pomocí Gitlabu",
|
||||||
"auth.login.google": "Přihlásit se pomocí Google",
|
"auth.login.google": "Přihlásit se pomocí Google",
|
||||||
|
@@ -2,10 +2,13 @@
|
|||||||
"auth.login": "Anmelden",
|
"auth.login": "Anmelden",
|
||||||
"auth.login.azure": "Mit Microsoft anmelden",
|
"auth.login.azure": "Mit Microsoft anmelden",
|
||||||
"auth.login.bitbucket": "Mit Bitbucket anmelden",
|
"auth.login.bitbucket": "Mit Bitbucket anmelden",
|
||||||
|
"auth.login.clerk": "Mit Clerk anmelden",
|
||||||
|
"auth.login.discord": "Mit Discord anmelden",
|
||||||
"auth.login.github": "Mit GitHub anmelden",
|
"auth.login.github": "Mit GitHub anmelden",
|
||||||
"auth.login.gitlab": "Mit GitLab anmelden",
|
"auth.login.gitlab": "Mit GitLab anmelden",
|
||||||
"auth.login.google": "Mit Google anmelden",
|
"auth.login.google": "Mit Google anmelden",
|
||||||
"auth.login.infomaniak": "Mit Infomaniak anmelden",
|
"auth.login.infomaniak": "Mit Infomaniak anmelden",
|
||||||
|
"auth.login.zitadel": "Mit Zitadel anmelden",
|
||||||
"auth.already_registered": "Bereits registriert?",
|
"auth.already_registered": "Bereits registriert?",
|
||||||
"auth.confirm_password": "Passwort bestätigen",
|
"auth.confirm_password": "Passwort bestätigen",
|
||||||
"auth.forgot_password": "Passwort vergessen",
|
"auth.forgot_password": "Passwort vergessen",
|
||||||
|
@@ -3,10 +3,13 @@
|
|||||||
"auth.login.authentik": "Login with Authentik",
|
"auth.login.authentik": "Login with Authentik",
|
||||||
"auth.login.azure": "Login with Microsoft",
|
"auth.login.azure": "Login with Microsoft",
|
||||||
"auth.login.bitbucket": "Login with Bitbucket",
|
"auth.login.bitbucket": "Login with Bitbucket",
|
||||||
|
"auth.login.clerk": "Login with Clerk",
|
||||||
|
"auth.login.discord": "Login with Discord",
|
||||||
"auth.login.github": "Login with GitHub",
|
"auth.login.github": "Login with GitHub",
|
||||||
"auth.login.gitlab": "Login with Gitlab",
|
"auth.login.gitlab": "Login with Gitlab",
|
||||||
"auth.login.google": "Login with Google",
|
"auth.login.google": "Login with Google",
|
||||||
"auth.login.infomaniak": "Login with Infomaniak",
|
"auth.login.infomaniak": "Login with Infomaniak",
|
||||||
|
"auth.login.zitadel": "Login with Zitadel",
|
||||||
"auth.already_registered": "Already registered?",
|
"auth.already_registered": "Already registered?",
|
||||||
"auth.confirm_password": "Confirm password",
|
"auth.confirm_password": "Confirm password",
|
||||||
"auth.forgot_password": "Forgot password",
|
"auth.forgot_password": "Forgot password",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "Iniciar Sesión",
|
"auth.login": "Iniciar Sesión",
|
||||||
"auth.login.azure": "Acceder con Microsoft",
|
"auth.login.azure": "Acceder con Microsoft",
|
||||||
"auth.login.bitbucket": "Acceder con Bitbucket",
|
"auth.login.bitbucket": "Acceder con Bitbucket",
|
||||||
|
"auth.login.clerk": "Acceder con Clerk",
|
||||||
|
"auth.login.discord": "Acceder con Discord",
|
||||||
"auth.login.github": "Acceder con GitHub",
|
"auth.login.github": "Acceder con GitHub",
|
||||||
"auth.login.gitlab": "Acceder con Gitlab",
|
"auth.login.gitlab": "Acceder con Gitlab",
|
||||||
"auth.login.google": "Acceder con Google",
|
"auth.login.google": "Acceder con Google",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "ورود",
|
"auth.login": "ورود",
|
||||||
"auth.login.azure": "ورود با مایکروسافت",
|
"auth.login.azure": "ورود با مایکروسافت",
|
||||||
"auth.login.bitbucket": "ورود با Bitbucket",
|
"auth.login.bitbucket": "ورود با Bitbucket",
|
||||||
|
"auth.login.clerk": "ورود با Clerk",
|
||||||
|
"auth.login.discord": "ورود با Discord",
|
||||||
"auth.login.github": "ورود با گیت هاب",
|
"auth.login.github": "ورود با گیت هاب",
|
||||||
"auth.login.gitlab": "ورود با گیت لب",
|
"auth.login.gitlab": "ورود با گیت لب",
|
||||||
"auth.login.google": "ورود با گوگل",
|
"auth.login.google": "ورود با گوگل",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "Connexion avec Authentik",
|
"auth.login.authentik": "Connexion avec Authentik",
|
||||||
"auth.login.azure": "Connexion avec Microsoft",
|
"auth.login.azure": "Connexion avec Microsoft",
|
||||||
"auth.login.bitbucket": "Connexion avec Bitbucket",
|
"auth.login.bitbucket": "Connexion avec Bitbucket",
|
||||||
|
"auth.login.clerk": "Connexion avec Clerk",
|
||||||
|
"auth.login.discord": "Connexion avec Discord",
|
||||||
"auth.login.github": "Connexion avec GitHub",
|
"auth.login.github": "Connexion avec GitHub",
|
||||||
"auth.login.gitlab": "Connexion avec Gitlab",
|
"auth.login.gitlab": "Connexion avec Gitlab",
|
||||||
"auth.login.google": "Connexion avec Google",
|
"auth.login.google": "Connexion avec Google",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "Masuk dengan Authentik",
|
"auth.login.authentik": "Masuk dengan Authentik",
|
||||||
"auth.login.azure": "Masuk dengan Microsoft",
|
"auth.login.azure": "Masuk dengan Microsoft",
|
||||||
"auth.login.bitbucket": "Masuk dengan Bitbucket",
|
"auth.login.bitbucket": "Masuk dengan Bitbucket",
|
||||||
|
"auth.login.clerk": "Masuk dengan Clerk",
|
||||||
|
"auth.login.discord": "Masuk dengan Discord",
|
||||||
"auth.login.github": "Masuk dengan GitHub",
|
"auth.login.github": "Masuk dengan GitHub",
|
||||||
"auth.login.gitlab": "Masuk dengan Gitlab",
|
"auth.login.gitlab": "Masuk dengan Gitlab",
|
||||||
"auth.login.google": "Masuk dengan Google",
|
"auth.login.google": "Masuk dengan Google",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "Accedi con Authentik",
|
"auth.login.authentik": "Accedi con Authentik",
|
||||||
"auth.login.azure": "Accedi con Microsoft",
|
"auth.login.azure": "Accedi con Microsoft",
|
||||||
"auth.login.bitbucket": "Accedi con Bitbucket",
|
"auth.login.bitbucket": "Accedi con Bitbucket",
|
||||||
|
"auth.login.clerk": "Accedi con Clerk",
|
||||||
|
"auth.login.discord": "Accedi con Discord",
|
||||||
"auth.login.github": "Accedi con GitHub",
|
"auth.login.github": "Accedi con GitHub",
|
||||||
"auth.login.gitlab": "Accedi con Gitlab",
|
"auth.login.gitlab": "Accedi con Gitlab",
|
||||||
"auth.login.google": "Accedi con Google",
|
"auth.login.google": "Accedi con Google",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "ログイン",
|
"auth.login": "ログイン",
|
||||||
"auth.login.azure": "Microsoftでログイン",
|
"auth.login.azure": "Microsoftでログイン",
|
||||||
"auth.login.bitbucket": "Bitbucketでログイン",
|
"auth.login.bitbucket": "Bitbucketでログイン",
|
||||||
|
"auth.login.clerk": "Clerkでログイン",
|
||||||
|
"auth.login.discord": "Discordでログイン",
|
||||||
"auth.login.github": "GitHubでログイン",
|
"auth.login.github": "GitHubでログイン",
|
||||||
"auth.login.gitlab": "Gitlabでログイン",
|
"auth.login.gitlab": "Gitlabでログイン",
|
||||||
"auth.login.google": "Googleでログイン",
|
"auth.login.google": "Googleでログイン",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "Logg inn med Authentik",
|
"auth.login.authentik": "Logg inn med Authentik",
|
||||||
"auth.login.azure": "Logg inn med Microsoft",
|
"auth.login.azure": "Logg inn med Microsoft",
|
||||||
"auth.login.bitbucket": "Logg inn med Bitbucket",
|
"auth.login.bitbucket": "Logg inn med Bitbucket",
|
||||||
|
"auth.login.clerk": "Logg inn med Clerk",
|
||||||
|
"auth.login.discord": "Logg inn med Discord",
|
||||||
"auth.login.github": "Logg inn med GitHub",
|
"auth.login.github": "Logg inn med GitHub",
|
||||||
"auth.login.gitlab": "Logg inn med Gitlab",
|
"auth.login.gitlab": "Logg inn med Gitlab",
|
||||||
"auth.login.google": "Logg inn med Google",
|
"auth.login.google": "Logg inn med Google",
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
"auth.login.authentik": "Entrar com Authentik",
|
"auth.login.authentik": "Entrar com Authentik",
|
||||||
"auth.login.azure": "Entrar com Microsoft",
|
"auth.login.azure": "Entrar com Microsoft",
|
||||||
"auth.login.bitbucket": "Entrar com Bitbucket",
|
"auth.login.bitbucket": "Entrar com Bitbucket",
|
||||||
|
"auth.login.clerk": "Entrar com Clerk",
|
||||||
|
"auth.login.discord": "Entrar com Discord",
|
||||||
"auth.login.github": "Entrar com GitHub",
|
"auth.login.github": "Entrar com GitHub",
|
||||||
"auth.login.gitlab": "Entrar com Gitlab",
|
"auth.login.gitlab": "Entrar com Gitlab",
|
||||||
"auth.login.google": "Entrar com Google",
|
"auth.login.google": "Entrar com Google",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "Entrar",
|
"auth.login": "Entrar",
|
||||||
"auth.login.azure": "Entrar com Microsoft",
|
"auth.login.azure": "Entrar com Microsoft",
|
||||||
"auth.login.bitbucket": "Entrar com Bitbucket",
|
"auth.login.bitbucket": "Entrar com Bitbucket",
|
||||||
|
"auth.login.clerk": "Entrar com Clerk",
|
||||||
|
"auth.login.discord": "Entrar com Discord",
|
||||||
"auth.login.github": "Entrar com GitHub",
|
"auth.login.github": "Entrar com GitHub",
|
||||||
"auth.login.gitlab": "Entrar com Gitlab",
|
"auth.login.gitlab": "Entrar com Gitlab",
|
||||||
"auth.login.google": "Entrar com Google",
|
"auth.login.google": "Entrar com Google",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "Autentificare",
|
"auth.login": "Autentificare",
|
||||||
"auth.login.azure": "Autentificare prin Microsoft",
|
"auth.login.azure": "Autentificare prin Microsoft",
|
||||||
"auth.login.bitbucket": "Autentificare prin Bitbucket",
|
"auth.login.bitbucket": "Autentificare prin Bitbucket",
|
||||||
|
"auth.login.clerk": "Autentificare prin Clerk",
|
||||||
|
"auth.login.discord": "Autentificare prin Discord",
|
||||||
"auth.login.github": "Autentificare prin GitHub",
|
"auth.login.github": "Autentificare prin GitHub",
|
||||||
"auth.login.gitlab": "Autentificare prin Gitlab",
|
"auth.login.gitlab": "Autentificare prin Gitlab",
|
||||||
"auth.login.google": "Autentificare prin Google",
|
"auth.login.google": "Autentificare prin Google",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "Giriş",
|
"auth.login": "Giriş",
|
||||||
"auth.login.azure": "Microsoft ile Giriş Yap",
|
"auth.login.azure": "Microsoft ile Giriş Yap",
|
||||||
"auth.login.bitbucket": "Bitbucket ile Giriş Yap",
|
"auth.login.bitbucket": "Bitbucket ile Giriş Yap",
|
||||||
|
"auth.login.clerk": "Clerk ile Giriş Yap",
|
||||||
|
"auth.login.discord": "Discord ile Giriş Yap",
|
||||||
"auth.login.github": "GitHub ile Giriş Yap",
|
"auth.login.github": "GitHub ile Giriş Yap",
|
||||||
"auth.login.gitlab": "GitLab ile Giriş Yap",
|
"auth.login.gitlab": "GitLab ile Giriş Yap",
|
||||||
"auth.login.google": "Google ile Giriş Yap",
|
"auth.login.google": "Google ile Giriş Yap",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "Đăng Nhập",
|
"auth.login": "Đăng Nhập",
|
||||||
"auth.login.azure": "Đăng Nhập Bằng Microsoft",
|
"auth.login.azure": "Đăng Nhập Bằng Microsoft",
|
||||||
"auth.login.bitbucket": "Đăng Nhập Bằng Bitbucket",
|
"auth.login.bitbucket": "Đăng Nhập Bằng Bitbucket",
|
||||||
|
"auth.login.clerk": "Đăng Nhập Bằng Clerk",
|
||||||
|
"auth.login.discord": "Đăng Nhập Bằng Discord",
|
||||||
"auth.login.github": "Đăng Nhập Bằng GitHub",
|
"auth.login.github": "Đăng Nhập Bằng GitHub",
|
||||||
"auth.login.gitlab": "Đăng Nhập Bằng Gitlab",
|
"auth.login.gitlab": "Đăng Nhập Bằng Gitlab",
|
||||||
"auth.login.google": "Đăng Nhập Bằng Google",
|
"auth.login.google": "Đăng Nhập Bằng Google",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "登录",
|
"auth.login": "登录",
|
||||||
"auth.login.azure": "使用 Microsoft 登录",
|
"auth.login.azure": "使用 Microsoft 登录",
|
||||||
"auth.login.bitbucket": "使用 Bitbucket 登录",
|
"auth.login.bitbucket": "使用 Bitbucket 登录",
|
||||||
|
"auth.login.clerk": "使用 Clerk 登录",
|
||||||
|
"auth.login.discord": "使用 Discord 登录",
|
||||||
"auth.login.github": "使用 GitHub 登录",
|
"auth.login.github": "使用 GitHub 登录",
|
||||||
"auth.login.gitlab": "使用 Gitlab 登录",
|
"auth.login.gitlab": "使用 Gitlab 登录",
|
||||||
"auth.login.google": "使用 Google 登录",
|
"auth.login.google": "使用 Google 登录",
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
"auth.login": "登入",
|
"auth.login": "登入",
|
||||||
"auth.login.azure": "使用 Microsoft 登入",
|
"auth.login.azure": "使用 Microsoft 登入",
|
||||||
"auth.login.bitbucket": "使用 Bitbucket 登入",
|
"auth.login.bitbucket": "使用 Bitbucket 登入",
|
||||||
|
"auth.login.clerk": "使用 Clerk 登入",
|
||||||
|
"auth.login.discord": "使用 Discord 登入",
|
||||||
"auth.login.github": "使用 GitHub 登入",
|
"auth.login.github": "使用 GitHub 登入",
|
||||||
"auth.login.gitlab": "使用 Gitlab 登入",
|
"auth.login.gitlab": "使用 Gitlab 登入",
|
||||||
"auth.login.google": "使用 Google 登入",
|
"auth.login.google": "使用 Google 登入",
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.419"
|
"version": "4.0.0-beta.420"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.420"
|
"version": "4.0.0-beta.421"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.8"
|
"version": "1.0.8"
|
||||||
|
BIN
public/heart.png
Normal file
BIN
public/heart.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 842 B After Width: | Height: | Size: 842 B |
1
public/svgs/miniflux.svg
Normal file
1
public/svgs/miniflux.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 431.27 298.29"><title>icon</title><path d="M178.24,117a95.75,95.75,0,0,1,45.48-11q50.36,0,64.16,43.8a139.1,139.1,0,0,1,37.57-31.07q22.13-12.73,49.34-12.73,37.77,0,54,23.08T445,198.38v174c0,4.35.61,7.3,1.83,8.88s3.86,3,7.92,4.14L469,390.14v10.65H384.53q-11,0-15.83-8.29t-4.88-24.86V187.73q0-26.64-5.89-37.88T338.24,138.6q-21.93,0-46.7,26A210,210,0,0,1,294,198.38v174c0,4.35.61,7.3,1.83,8.88s3.86,3,7.92,4.14l14.21,4.74v10.65H233.47q-11,0-15.84-8.29t-4.88-24.86V187.73q0-26.64-5.88-37.88t-19.7-11.25q-21.53,0-44.26,23.68v210.1c0,4.35.6,7.4,1.82,9.17s3.72,3.26,7.52,4.44l13.8,4.15v10.65H37.73V390.14l14.21-4.74q6.09-1.77,7.92-4.14c1.22-1.58,1.83-4.53,1.83-8.88V148.67q0-6.51-1.83-8.88t-7.92-4.15l-14.21-4.73V120.26l97.46-17.76h6.9v41.43A142.53,142.53,0,0,1,178.24,117Z" transform="translate(-37.73 -102.5)"/></svg>
|
After Width: | Height: | Size: 897 B |
1
public/svgs/pingvinshare.svg
Normal file
1
public/svgs/pingvinshare.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 943.11 911.62"><ellipse cx="471.56" cy="454.28" rx="471.56" ry="454.28" fill="#46509e"/><ellipse cx="471.56" cy="390.28" rx="233.66" ry="207" fill="#37474f"/><path d="M705.22,849c-36.69,21.14-123.09,64.32-240.64,62.57A469.81,469.81,0,0,1,237.89,849V394.76H705.22Z" fill="#37474f"/><path d="M658.81,397.7V873.49a478.12,478.12,0,0,1-374.19,0V397.7c0-95.55,83.78-173,187.1-173S658.81,302.15,658.81,397.7Z" fill="#fff"/><polygon points="565.02 431.68 471.56 514.49 378.09 431.68 565.02 431.68" fill="#46509e"/><ellipse cx="378.09" cy="369.58" rx="23.37" ry="20.7" fill="#37474f"/><ellipse cx="565.02" cy="369.58" rx="23.37" ry="20.7" fill="#37474f"/><path d="M658.49,400.63c0-40-36.6-72.45-81.79-72.45s-81.78,32.41-81.78,72.45a64.79,64.79,0,0,0,7.9,31.05H440.29a64.79,64.79,0,0,0,7.9-31.05c0-40-36.59-72.45-81.78-72.45s-81.79,32.41-81.79,72.45l-46.73-10.35c0-114.31,104.64-207,233.67-207s233.66,92.69,233.66,207Z" fill="#37474f"/></svg>
|
After Width: | Height: | Size: 1018 B |
@@ -122,7 +122,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@utility navbar-main {
|
@utility navbar-main {
|
||||||
@apply flex flex-col gap-4 justify-items-start pb-2 border-b-2 border-solid h-fit md:flex-row sm:justify-between dark:border-coolgray-200 md:items-center;
|
@apply flex flex-col gap-4 justify-items-start pb-2 border-b-2 border-solid h-fit md:flex-row sm:justify-between dark:border-coolgray-200 border-neutral-200 md:items-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@utility loading {
|
@utility loading {
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
@if (isset($text))
|
@if (isset($text))
|
||||||
<div>{{ $text }}</div>
|
<div>{{ $text }}</div>
|
||||||
@endif
|
@endif
|
||||||
<svg class="w-4 h-4 mx-1 ml-3 text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
|
<svg class="w-4 h-4 mx-1 ml-3 dark:text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
<path class="opacity-75" fill="currentColor"
|
<path class="opacity-75" fill="currentColor"
|
||||||
|
@@ -344,7 +344,7 @@
|
|||||||
@if (isInstanceAdmin() || session('impersonating'))
|
@if (isInstanceAdmin() || session('impersonating'))
|
||||||
<li>
|
<li>
|
||||||
<a title="Admin" class="menu-item" href="/admin">
|
<a title="Admin" class="menu-item" href="/admin">
|
||||||
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
|
<svg class="text-pink-500 icon" viewBox="0 0 256 256"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
|
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
|
||||||
@@ -362,7 +362,7 @@
|
|||||||
</li>
|
</li>
|
||||||
@endpersist
|
@endpersist
|
||||||
@endif
|
@endif
|
||||||
<li>
|
{{-- <li>
|
||||||
<a title="Onboarding"
|
<a title="Onboarding"
|
||||||
class="{{ request()->is('onboarding*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('onboarding*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('onboarding') }}">
|
href="{{ route('onboarding') }}">
|
||||||
@@ -372,7 +372,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Onboarding
|
Onboarding
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li> --}}
|
||||||
<li>
|
<li>
|
||||||
<a title="Sponsor us" class="menu-item" href="https://coolify.io/sponsorships"
|
<a title="Sponsor us" class="menu-item" href="https://coolify.io/sponsorships"
|
||||||
target="_blank">
|
target="_blank">
|
||||||
@@ -410,7 +410,7 @@
|
|||||||
<form action="/logout" method="POST">
|
<form action="/logout" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
<button title="Logout" type="submit" class="gap-2 mb-6 menu-item">
|
<button title="Logout" type="submit" class="gap-2 mb-6 menu-item">
|
||||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon mr-1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22m7-6v-3h-8v-2h8V8l5 4z" />
|
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22m7-6v-3h-8v-2h8V8l5 4z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@@ -6,21 +6,25 @@
|
|||||||
x-transition:enter-start="translate-y-full" x-transition:enter-end="translate-y-0"
|
x-transition:enter-start="translate-y-full" x-transition:enter-end="translate-y-0"
|
||||||
x-transition:leave="transition ease-in duration-300" x-transition:leave-start="translate-y-0"
|
x-transition:leave="transition ease-in duration-300" x-transition:leave-start="translate-y-0"
|
||||||
x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);"
|
x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);"
|
||||||
class="fixed bottom-0 right-0 w-full h-auto duration-300 ease-out sm:px-5 sm:pb-5 w-full z-999"
|
class="fixed bottom-0 right-0 w-full h-auto duration-300 ease-out sm:px-5 sm:pb-5 w-full z-999" x-cloak>
|
||||||
x-cloak>
|
@isset($customActions)
|
||||||
|
{{ $customActions }}
|
||||||
|
@else
|
||||||
<div
|
<div
|
||||||
class="flex items-center flex-col justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded-sm">
|
class="flex lg:items-center flex-col justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 border-neutral-200 dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded-sm">
|
||||||
<div
|
<div
|
||||||
class="flex flex-col items-start h-full pb-6 text-xs lg:items-center lg:flex-row lg:pb-0 lg:pr-6 lg:space-x-5 dark:text-neutral-300">
|
class="flex flex-col items-start h-full pb-6 text-xs lg:items-center lg:flex-row lg:pb-0 lg:pr-6 lg:space-x-5 dark:text-neutral-300">
|
||||||
@if (isset($icon))
|
@if (isset($icon))
|
||||||
|
<div class="hidden lg:block">
|
||||||
{{ $icon }}
|
{{ $icon }}
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="pt-6 lg:pt-0">
|
<div class="pt-6 lg:pt-0">
|
||||||
<h4 class="w-full mb-1 text-xl font-bold leading-none -translate-y-1 text-neutral-900 dark:text-white">
|
<h4 class="w-full mb-1 text-xl font-bold leading-none -translate-y-1 text-neutral-900 dark:text-white">
|
||||||
{{ $title }}
|
{{ $title }}
|
||||||
</h4>
|
</h4>
|
||||||
<p class="">{{ $description }}</span></p>
|
<p>{{ $description }}</span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -31,4 +35,6 @@
|
|||||||
{{ $buttonText }}
|
{{ $buttonText }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@endisset
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -13,11 +13,18 @@
|
|||||||
<x-status.stopped :status="$resource->status" />
|
<x-status.stopped :status="$resource->status" />
|
||||||
@endif
|
@endif
|
||||||
@if (!str($resource->status)->contains('exited') && $showRefreshButton)
|
@if (!str($resource->status)->contains('exited') && $showRefreshButton)
|
||||||
<button title="Refresh Status" wire:click='checkStatus'
|
<button wire:loading.remove title="Refresh Status" wire:click='checkStatus'
|
||||||
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
|
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
|
||||||
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
d="M12 2a10.016 10.016 0 0 0-7 2.877V3a1 1 0 1 0-2 0v4.5a1 1 0 0 0 1 1h4.5a1 1 0 0 0 0-2H6.218A7.98 7.98 0 0 1 20 12a1 1 0 0 0 2 0A10.012 10.012 0 0 0 12 2zm7.989 13.5h-4.5a1 1 0 0 0 0 2h2.293A7.98 7.98 0 0 1 4 12a1 1 0 0 0-2 0a9.986 9.986 0 0 0 16.989 7.133V21a1 1 0 0 0 2 0v-4.5a1 1 0 0 0-1-1z" />
|
d="M12 2a10.016 10.016 0 0 0-7 2.877V3a1 1 0 1 0-2 0v4.5a1 1 0 0 0 1 1h4.5a1 1 0 0 0 0-2H6.218A7.98 7.98 0 0 1 20 12a1 1 0 0 0 2 0A10.012 10.012 0 0 0 12 2zm7.989 13.5h-4.5a1 1 0 0 0 0 2h2.293A7.98 7.98 0 0 1 4 12a1 1 0 0 0-2 0a9.986 9.986 0 0 0 16.989 7.133V21a1 1 0 0 0 2 0v-4.5a1 1 0 0 0-1-1z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button wire:loading title="Refreshing Status" wire:click='checkStatus'
|
||||||
|
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
|
||||||
|
<svg class="w-4 h-4 animate-spin" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M12 2a10.016 10.016 0 0 0-7 2.877V3a1 1 0 1 0-2 0v4.5a1 1 0 0 0 1 1h4.5a1 1 0 0 0 0-2H6.218A7.98 7.98 0 0 1 20 12a1 1 0 0 0 2 0A10.012 10.012 0 0 0 12 2zm7.989 13.5h-4.5a1 1 0 0 0 0 2h2.293A7.98 7.98 0 0 1 4 12a1 1 0 0 0-2 0a9.986 9.986 0 0 0 16.989 7.133V21a1 1 0 0 0 2 0v-4.5a1 1 0 0 0-1-1z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
@endif
|
@endif
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
@if (str($complexStatus)->contains('running'))
|
@if (str($complexStatus)->contains('running'))
|
||||||
<x-status.running :status="$complexStatus" />
|
<x-status.running :status="$complexStatus" />
|
||||||
|
@elseif(str($complexStatus)->contains('starting'))
|
||||||
|
<x-status.restarting :status="$complexStatus" />
|
||||||
@elseif(str($complexStatus)->contains('restarting'))
|
@elseif(str($complexStatus)->contains('restarting'))
|
||||||
<x-status.restarting :status="$complexStatus" />
|
<x-status.restarting :status="$complexStatus" />
|
||||||
@elseif(str($complexStatus)->contains('degraded'))
|
@elseif(str($complexStatus)->contains('degraded'))
|
||||||
@@ -8,11 +10,18 @@
|
|||||||
<x-status.stopped :status="$complexStatus" />
|
<x-status.stopped :status="$complexStatus" />
|
||||||
@endif
|
@endif
|
||||||
@if (!str($complexStatus)->contains('exited') && $showRefreshButton)
|
@if (!str($complexStatus)->contains('exited') && $showRefreshButton)
|
||||||
<button title="Refresh Status" wire:click='checkStatus'
|
<button wire:loading.remove title="Refresh Status" wire:click='checkStatus'
|
||||||
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
|
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
|
||||||
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
d="M12 2a10.016 10.016 0 0 0-7 2.877V3a1 1 0 1 0-2 0v4.5a1 1 0 0 0 1 1h4.5a1 1 0 0 0 0-2H6.218A7.98 7.98 0 0 1 20 12a1 1 0 0 0 2 0A10.012 10.012 0 0 0 12 2zm7.989 13.5h-4.5a1 1 0 0 0 0 2h2.293A7.98 7.98 0 0 1 4 12a1 1 0 0 0-2 0a9.986 9.986 0 0 0 16.989 7.133V21a1 1 0 0 0 2 0v-4.5a1 1 0 0 0-1-1z" />
|
d="M12 2a10.016 10.016 0 0 0-7 2.877V3a1 1 0 1 0-2 0v4.5a1 1 0 0 0 1 1h4.5a1 1 0 0 0 0-2H6.218A7.98 7.98 0 0 1 20 12a1 1 0 0 0 2 0A10.012 10.012 0 0 0 12 2zm7.989 13.5h-4.5a1 1 0 0 0 0 2h2.293A7.98 7.98 0 0 1 4 12a1 1 0 0 0-2 0a9.986 9.986 0 0 0 16.989 7.133V21a1 1 0 0 0 2 0v-4.5a1 1 0 0 0-1-1z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button wire:loading title="Refreshing Status" wire:click='checkStatus'
|
||||||
|
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
|
||||||
|
<svg class="w-4 h-4 animate-spin" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M12 2a10.016 10.016 0 0 0-7 2.877V3a1 1 0 1 0-2 0v4.5a1 1 0 0 0 1 1h4.5a1 1 0 0 0 0-2H6.218A7.98 7.98 0 0 1 20 12a1 1 0 0 0 2 0A10.012 10.012 0 0 0 12 2zm7.989 13.5h-4.5a1 1 0 0 0 0 2h2.293A7.98 7.98 0 0 1 4 12a1 1 0 0 0-2 0a9.986 9.986 0 0 0 16.989 7.133V21a1 1 0 0 0 2 0v-4.5a1 1 0 0 0-1-1z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
@endif
|
@endif
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
<section class="flex flex-col h-full lg:items-center lg:justify-center">
|
<section class="flex flex-col h-full lg:items-center lg:justify-center">
|
||||||
<div
|
<div
|
||||||
class="flex flex-col items-center justify-center p-10 mx-2 mt-10 bg-white border rounded-lg shadow-sm lg:p-20 dark:bg-transparent dark:border-none max-w-7xl ">
|
class="flex flex-col items-center justify-center p-10 mx-2 mt-10 bg-white border rounded-lg shadow-sm lg:p-20 dark:bg-transparent dark:border-none max-w-7xl border-neutral-200">
|
||||||
@if ($currentState === 'welcome')
|
@if ($currentState === 'welcome')
|
||||||
<h1 class="text-3xl font-bold lg:text-5xl">Welcome to Coolify</h1>
|
<h1 class="text-3xl font-bold lg:text-5xl">Welcome to Coolify</h1>
|
||||||
<div class="py-6 text-center lg:text-xl">Let me help you set up the basics.</div>
|
<div class="py-6 text-center lg:text-xl">Let me help you set up the basics.</div>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
Git
|
Git
|
||||||
integrations, deploy databases and services, monitor these resources with notifications and
|
integrations, deploy databases and services, monitor these resources with notifications and
|
||||||
alerts
|
alerts
|
||||||
without vendor lock-in. <br/>
|
without vendor lock-in. <br />
|
||||||
<a href="https://coolify.io" class="dark:text-white hover:underline">Coolify Home</a>.
|
<a href="https://coolify.io" class="dark:text-white hover:underline">Coolify Home</a>.
|
||||||
<br><br>
|
<br><br>
|
||||||
<span class="text-xl">
|
<span class="text-xl">
|
||||||
@@ -277,8 +277,7 @@
|
|||||||
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
|
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
|
||||||
label="Name" id="remoteServerName" wire:model="remoteServerName" />
|
label="Name" id="remoteServerName" wire:model="remoteServerName" />
|
||||||
<x-forms.input placeholder="Description, so others will know more about this."
|
<x-forms.input placeholder="Description, so others will know more about this."
|
||||||
label="Description" id="remoteServerDescription"
|
label="Description" id="remoteServerDescription" wire:model="remoteServerDescription" />
|
||||||
wire:model="remoteServerDescription" />
|
|
||||||
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
|
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
|
||||||
wire:model="remoteServerHost" />
|
wire:model="remoteServerHost" />
|
||||||
<div x-data="{ showAdvanced: false }" class="flex flex-col gap-2">
|
<div x-data="{ showAdvanced: false }" class="flex flex-col gap-2">
|
||||||
@@ -323,7 +322,8 @@
|
|||||||
</x-slot:actions>
|
</x-slot:actions>
|
||||||
<x-slot:explanation>
|
<x-slot:explanation>
|
||||||
<p>This will install the latest Docker Engine on your server, configure a few things to be able
|
<p>This will install the latest Docker Engine on your server, configure a few things to be able
|
||||||
to run optimal.<br><br>Minimum Docker Engine version is: {{ $minDockerVersion }}<br><br>To manually install
|
to run optimal.<br><br>Minimum Docker Engine version is: {{ $minDockerVersion }}<br><br>To
|
||||||
|
manually install
|
||||||
Docker
|
Docker
|
||||||
Engine, check <a target="_blank" class="underline dark:text-warning"
|
Engine, check <a target="_blank" class="underline dark:text-warning"
|
||||||
href="https://docs.docker.com/engine/install/#server">this
|
href="https://docs.docker.com/engine/install/#server">this
|
||||||
|
@@ -17,9 +17,9 @@
|
|||||||
<section>
|
<section>
|
||||||
<h3 class="pb-2">Projects</h3>
|
<h3 class="pb-2">Projects</h3>
|
||||||
@if ($projects->count() > 0)
|
@if ($projects->count() > 0)
|
||||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||||
@foreach ($projects as $project)
|
@foreach ($projects as $project)
|
||||||
<div class="gap-2 border border-transparent cursor-pointer box group"
|
<div class="gap-2 border cursor-pointer box group"
|
||||||
wire:click="navigateToProject('{{ $project->uuid }}')">
|
wire:click="navigateToProject('{{ $project->uuid }}')">
|
||||||
<div class="flex flex-1 mx-6">
|
<div class="flex flex-1 mx-6">
|
||||||
<div class="flex flex-col justify-center flex-1">
|
<div class="flex flex-col justify-center flex-1">
|
||||||
@@ -68,7 +68,6 @@
|
|||||||
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
||||||
@class([
|
@class([
|
||||||
'gap-2 border cursor-pointer box group',
|
'gap-2 border cursor-pointer box group',
|
||||||
'border-transparent' => $server->settings->is_reachable,
|
|
||||||
'border-red-500' => !$server->settings->is_reachable,
|
'border-red-500' => !$server->settings->is_reachable,
|
||||||
])>
|
])>
|
||||||
<div class="flex flex-col justify-center mx-6">
|
<div class="flex flex-col justify-center mx-6">
|
||||||
@@ -124,7 +123,7 @@
|
|||||||
|
|
||||||
@if ($servers->count() > 0 && $projects->count() > 0)
|
@if ($servers->count() > 0 && $projects->count() > 0)
|
||||||
<section>
|
<section>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<h3 class="pb-2">Deployments</h3>
|
<h3 class="pb-2">Deployments</h3>
|
||||||
@if (count($deploymentsPerServer) > 0)
|
@if (count($deploymentsPerServer) > 0)
|
||||||
<x-loading />
|
<x-loading />
|
||||||
|
@@ -4,9 +4,10 @@
|
|||||||
notification: true,
|
notification: true,
|
||||||
realtime: false,
|
realtime: false,
|
||||||
},
|
},
|
||||||
|
isDevelopment: {{ isDev() ? 'true' : 'false' }},
|
||||||
init() {
|
init() {
|
||||||
this.popups.sponsorship = localStorage.getItem('popupSponsorship') !== 'false';
|
this.popups.sponsorship = this.shouldShowMonthlyPopup('popupSponsorship');
|
||||||
this.popups.notification = localStorage.getItem('popupNotification') !== 'false';
|
this.popups.notification = this.shouldShowMonthlyPopup('popupNotification');
|
||||||
this.popups.realtime = localStorage.getItem('popupRealtime');
|
this.popups.realtime = localStorage.getItem('popupRealtime');
|
||||||
|
|
||||||
let checkNumber = 1;
|
let checkNumber = 1;
|
||||||
@@ -31,6 +32,35 @@
|
|||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
shouldShowMonthlyPopup(storageKey) {
|
||||||
|
const disabledTimestamp = localStorage.getItem(storageKey);
|
||||||
|
|
||||||
|
// If never disabled, show the popup
|
||||||
|
if (!disabledTimestamp || disabledTimestamp === 'false') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If disabled timestamp is not a valid number, show the popup
|
||||||
|
const disabledTime = parseInt(disabledTimestamp);
|
||||||
|
if (isNaN(disabledTime)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const disabledDate = new Date(disabledTime);
|
||||||
|
|
||||||
|
{{-- if (this.isDevelopment) {
|
||||||
|
// In development: check if 10 seconds have passed
|
||||||
|
const timeDifference = now.getTime() - disabledDate.getTime();
|
||||||
|
const tenSecondsInMs = 10 * 1000;
|
||||||
|
return timeDifference >= tenSecondsInMs;
|
||||||
|
} else { --}}
|
||||||
|
// In production: check if we're in a different month or year
|
||||||
|
const isDifferentMonth = now.getMonth() !== disabledDate.getMonth() ||
|
||||||
|
now.getFullYear() !== disabledDate.getFullYear();
|
||||||
|
return isDifferentMonth;
|
||||||
|
{{-- } --}}
|
||||||
}
|
}
|
||||||
}">
|
}">
|
||||||
@auth
|
@auth
|
||||||
@@ -56,35 +86,60 @@
|
|||||||
@endif
|
@endif
|
||||||
</span>
|
</span>
|
||||||
@endauth
|
@endauth
|
||||||
|
@if (instanceSettings()->is_sponsorship_popup_enabled && !isCloud())
|
||||||
<span x-show="popups.sponsorship">
|
<span x-show="popups.sponsorship">
|
||||||
<x-popup>
|
<x-popup>
|
||||||
<x-slot:title>
|
<x-slot:customActions>
|
||||||
Love Coolify as we do?
|
<div
|
||||||
</x-slot:title>
|
class="flex md:flex-row flex-col max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 border-neutral-200 dark:bg-coolgray-100 lg:p-8 lg:pb-4 sm:rounded-sm gap-2">
|
||||||
<x-slot:icon>
|
<div class="md:block hidden">
|
||||||
<img src="https://cdn-icons-png.flaticon.com/512/8236/8236748.png"
|
<img src="{{ asset('heart.png') }}" class="w-20 h-20">
|
||||||
class="w-8 h-8 sm:w-12 sm:h-12 lg:w-16 lg:h-16">
|
</div>
|
||||||
</x-slot:icon>
|
<div class="flex flex-col gap-2 lg:px-10 px-1">
|
||||||
<x-slot:description>
|
<div class="lg:text-xl text-md dark:text-white font-bold">Love Coolify? Support our work.
|
||||||
<span>Please
|
</div>
|
||||||
consider donating on <a href="https://github.com/sponsors/coollabsio"
|
<div class="lg:text-sm text-xs dark:text-white">
|
||||||
class="text-xs underline dark:text-white">GitHub</a> or <a
|
We are already profitable thanks to <span class="font-bold text-pink-500">YOU</span>
|
||||||
href="https://opencollective.com/coollabsio"
|
but...<br />We
|
||||||
class="text-xs underline dark:text-white">OpenCollective</a>.<br><br></span>
|
would
|
||||||
<span>It enables us to keep creating features without paywalls, ensuring our work remains free and
|
like to
|
||||||
open.</span>
|
make
|
||||||
</x-slot:description>
|
more cool features.
|
||||||
<x-slot:button-text @click="disableSponsorship()">
|
</div>
|
||||||
Disable This Popup
|
<div class="lg:text-sm text-xs dark:text-white pt-2 ">
|
||||||
</x-slot:button-text>
|
For this we need your help to support our work financially.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 text-center md:mx-auto lg:py-0 pt-2">
|
||||||
|
<x-forms.button isHighlighted class="md:w-36 w-full"><a target="_blank"
|
||||||
|
href="https://github.com/sponsors/coollabsio"
|
||||||
|
class="font-bold dark:text-white">GitHub
|
||||||
|
Sponsors</a></x-forms.button>
|
||||||
|
<x-forms.button isHighlighted class="md:w-36 w-full"><a target="_blank"
|
||||||
|
href="https://opencollective.com/coollabsio/donate?interval=month&amount=10&name=&legalName=&email="
|
||||||
|
class="font-bold dark:text-white">Open
|
||||||
|
Collective</a></x-forms.button>
|
||||||
|
<x-forms.button isHighlighted class="md:w-36 w-full"><a
|
||||||
|
href="https://donate.stripe.com/8x2bJ104ifmB9kB45u38402" target="_blank"
|
||||||
|
class="font-bold dark:text-white">Stripe</a></x-forms.button>
|
||||||
|
<div class="pt-4 dark:text-white hover:underline cursor-pointer lg:text-base text-xs"
|
||||||
|
@click="bannerVisible=false;disableSponsorship()">
|
||||||
|
Maybe next time
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-slot:customActions>
|
||||||
</x-popup>
|
</x-popup>
|
||||||
</span>
|
</span>
|
||||||
|
@endif
|
||||||
@if (currentTeam()->subscriptionPastOverDue())
|
@if (currentTeam()->subscriptionPastOverDue())
|
||||||
<x-banner :closable=false>
|
<x-banner :closable=false>
|
||||||
<div><span class="font-bold text-red-500">WARNING:</span> Your subscription is in over-due. If your latest
|
<div><span class="font-bold text-red-500">WARNING:</span> Your subscription is in over-due. If your
|
||||||
|
latest
|
||||||
payment is not paid within a week, all automations <span class="font-bold text-red-500">will
|
payment is not paid within a week, all automations <span class="font-bold text-red-500">will
|
||||||
be deactivated</span>. Visit <a href="{{ route('subscription.show') }}"
|
be deactivated</span>. Visit <a href="{{ route('subscription.show') }}"
|
||||||
class="underline dark:text-white">/subscription</a> to check your subscription status or pay your
|
class="underline dark:text-white">/subscription</a> to check your subscription status or pay
|
||||||
|
your
|
||||||
invoice (or check your email for the invoice).
|
invoice (or check your email for the invoice).
|
||||||
</div>
|
</div>
|
||||||
</x-banner>
|
</x-banner>
|
||||||
@@ -128,11 +183,13 @@
|
|||||||
@endif
|
@endif
|
||||||
<script>
|
<script>
|
||||||
function disableSponsorship() {
|
function disableSponsorship() {
|
||||||
localStorage.setItem('popupSponsorship', false);
|
// Store current timestamp instead of just 'false'
|
||||||
|
localStorage.setItem('popupSponsorship', Date.now().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableNotification() {
|
function disableNotification() {
|
||||||
localStorage.setItem('popupNotification', false);
|
// Store current timestamp instead of just 'false'
|
||||||
|
localStorage.setItem('popupNotification', Date.now().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableRealtime() {
|
function disableRealtime() {
|
||||||
|
@@ -35,7 +35,7 @@
|
|||||||
Select events for which you would like to receive Discord notifications.
|
Select events for which you would like to receive Discord notifications.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 max-w-2xl">
|
<div class="flex flex-col gap-4 max-w-2xl">
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Deployments</h3>
|
<h3 class="font-medium mb-3">Deployments</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessDiscordNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessDiscordNotifications"
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
id="statusChangeDiscordNotifications" label="Container Status Changes" />
|
id="statusChangeDiscordNotifications" label="Container Status Changes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Backups</h3>
|
<h3 class="font-medium mb-3">Backups</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessDiscordNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="backupSuccessDiscordNotifications"
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
label="Backup Failure" />
|
label="Backup Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessDiscordNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessDiscordNotifications"
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
label="Scheduled Task Failure" />
|
label="Scheduled Task Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Server</h3>
|
<h3 class="font-medium mb-3">Server</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessDiscordNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessDiscordNotifications"
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if (!$useInstanceEmailSettings)
|
@if (!$useInstanceEmailSettings)
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-2">
|
||||||
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
|
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
|
||||||
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails."
|
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails."
|
||||||
label="From Address" />
|
label="From Address" />
|
||||||
@@ -54,7 +54,8 @@
|
|||||||
@endif
|
@endif
|
||||||
@if (!$useInstanceEmailSettings)
|
@if (!$useInstanceEmailSettings)
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<form wire:submit='submitSmtp' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
|
<form wire:submit='submitSmtp'
|
||||||
|
class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h3>SMTP Server</h3>
|
<h3>SMTP Server</h3>
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
@@ -85,7 +86,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<form wire:submit='submitResend' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
|
<form wire:submit='submitResend'
|
||||||
|
class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h3>Resend</h3>
|
<h3>Resend</h3>
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
@@ -112,7 +114,7 @@
|
|||||||
Select events for which you would like to receive email notifications.
|
Select events for which you would like to receive email notifications.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 max-w-2xl">
|
<div class="flex flex-col gap-4 max-w-2xl">
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Deployments</h3>
|
<h3 class="font-medium mb-3">Deployments</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessEmailNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessEmailNotifications"
|
||||||
@@ -124,7 +126,7 @@
|
|||||||
id="statusChangeEmailNotifications" label="Container Status Changes" />
|
id="statusChangeEmailNotifications" label="Container Status Changes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Backups</h3>
|
<h3 class="font-medium mb-3">Backups</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessEmailNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="backupSuccessEmailNotifications"
|
||||||
@@ -133,7 +135,7 @@
|
|||||||
label="Backup Failure" />
|
label="Backup Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessEmailNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessEmailNotifications"
|
||||||
@@ -142,7 +144,7 @@
|
|||||||
label="Scheduled Task Failure" />
|
label="Scheduled Task Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Server</h3>
|
<h3 class="font-medium mb-3">Server</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessEmailNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessEmailNotifications"
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
Select events for which you would like to receive Pushover notifications.
|
Select events for which you would like to receive Pushover notifications.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 max-w-2xl">
|
<div class="flex flex-col gap-4 max-w-2xl">
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Deployments</h3>
|
<h3 class="font-medium mb-3">Deployments</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessPushoverNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessPushoverNotifications"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
id="statusChangePushoverNotifications" label="Container Status Changes" />
|
id="statusChangePushoverNotifications" label="Container Status Changes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Backups</h3>
|
<h3 class="font-medium mb-3">Backups</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessPushoverNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="backupSuccessPushoverNotifications"
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
label="Backup Failure" />
|
label="Backup Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessPushoverNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessPushoverNotifications"
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
label="Scheduled Task Failure" />
|
label="Scheduled Task Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Server</h3>
|
<h3 class="font-medium mb-3">Server</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessPushoverNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessPushoverNotifications"
|
||||||
|
@@ -32,7 +32,7 @@
|
|||||||
Select events for which you would like to receive Slack notifications.
|
Select events for which you would like to receive Slack notifications.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 max-w-2xl">
|
<div class="flex flex-col gap-4 max-w-2xl">
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Deployments</h3>
|
<h3 class="font-medium mb-3">Deployments</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessSlackNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessSlackNotifications"
|
||||||
@@ -44,14 +44,14 @@
|
|||||||
id="statusChangeSlackNotifications" label="Container Status Changes" />
|
id="statusChangeSlackNotifications" label="Container Status Changes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Backups</h3>
|
<h3 class="font-medium mb-3">Backups</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessSlackNotifications" label="Backup Success" />
|
<x-forms.checkbox instantSave="saveModel" id="backupSuccessSlackNotifications" label="Backup Success" />
|
||||||
<x-forms.checkbox instantSave="saveModel" id="backupFailureSlackNotifications" label="Backup Failure" />
|
<x-forms.checkbox instantSave="saveModel" id="backupFailureSlackNotifications" label="Backup Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessSlackNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessSlackNotifications"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
label="Scheduled Task Failure" />
|
label="Scheduled Task Failure" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="font-medium mb-3">Server</h3>
|
<h3 class="font-medium mb-3">Server</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessSlackNotifications"
|
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessSlackNotifications"
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
Select events for which you would like to receive Telegram notifications.
|
Select events for which you would like to receive Telegram notifications.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-4 ">
|
<div class="flex flex-col gap-4 ">
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="text-lg font-medium mb-3">Deployments</h3>
|
<h3 class="text-lg font-medium mb-3">Deployments</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<div class="pl-1 flex gap-2">
|
<div class="pl-1 flex gap-2">
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="text-lg font-medium mb-3">Backups</h3>
|
<h3 class="text-lg font-medium mb-3">Backups</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<div class="pl-1 flex gap-2">
|
<div class="pl-1 flex gap-2">
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="text-lg font-medium mb-3">Scheduled Tasks</h3>
|
<h3 class="text-lg font-medium mb-3">Scheduled Tasks</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<div class="pl-1 flex gap-2">
|
<div class="pl-1 flex gap-2">
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
<div class="border dark:border-coolgray-300 border-neutral-200 p-4 rounded-lg">
|
||||||
<h3 class="text-lg font-medium mb-3">Server</h3>
|
<h3 class="text-lg font-medium mb-3">Server</h3>
|
||||||
<div class="flex flex-col gap-1.5 pl-1">
|
<div class="flex flex-col gap-1.5 pl-1">
|
||||||
<div class="pl-1 flex gap-2">
|
<div class="pl-1 flex gap-2">
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
<h2>Change Password</h2>
|
<h2>Change Password</h2>
|
||||||
<x-forms.button type="submit" label="Save">Save</x-forms.button>
|
<x-forms.button type="submit" label="Save">Save</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs font-bold text-warning pb-2">Resetting the password will logout all sessions.</div>
|
<div class="text-xs font-bold dark:text-warning pb-2">Resetting the password will logout all sessions.</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<x-forms.input id="current_password" label="Current Password" required type="password" />
|
<x-forms.input id="current_password" label="Current Password" required type="password" />
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
@@ -8,16 +8,16 @@
|
|||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.application.deployment.index', $parameters) }}">
|
href="{{ route('project.application.deployment.index', $parameters) }}">
|
||||||
<button>Deployments</button>
|
Deployments
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('project.application.logs') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.logs') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.application.logs', $parameters) }}">
|
href="{{ route('project.application.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
Logs
|
||||||
</a>
|
</a>
|
||||||
@if (!$application->destination->server->isSwarm())
|
@if (!$application->destination->server->isSwarm())
|
||||||
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.application.command', $parameters) }}">
|
href="{{ route('project.application.command', $parameters) }}">
|
||||||
<button>Terminal</button>
|
Terminal
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<x-applications.links :application="$application" />
|
<x-applications.links :application="$application" />
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pb-4 ">You can easily rollback to a previously built (local) images
|
<div class="pb-4 ">You can easily rollback to a previously built (local) images
|
||||||
quickly.</div>
|
quickly.</div>
|
||||||
<div wire:target='loadImages'>
|
<div wire:target='loadImages' wire:loading.remove>
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
@forelse ($images as $image)
|
@forelse ($images as $image)
|
||||||
<div class="w-2/4 p-2">
|
<div class="w-2/4 p-2">
|
||||||
<div class="bg-white border rounded-sm dark:border-black dark:bg-coolgray-100">
|
<div class="bg-white border rounded-sm dark:border-black dark:bg-coolgray-100 border-neutral-200">
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<div class="">
|
<div class="">
|
||||||
@if (data_get($image, 'is_current'))
|
@if (data_get($image, 'is_current'))
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
Rollback
|
Rollback
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@else
|
@else
|
||||||
<x-forms.button class="bg-coolgray-100"
|
<x-forms.button class="dark:bg-coolgray-100"
|
||||||
wire:click="rollbackImage('{{ data_get($image, 'tag') }}')">
|
wire:click="rollbackImage('{{ data_get($image, 'tag') }}')">
|
||||||
Rollback
|
Rollback
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@@ -44,4 +44,5 @@
|
|||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div wire:target='loadImages' wire:loading>Loading available docker images...</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<h1>Backups</h1>
|
<h1>Backups</h1>
|
||||||
<livewire:project.shared.configuration-checker :resource="$database" />
|
<livewire:project.shared.configuration-checker :resource="$database" />
|
||||||
<livewire:project.database.heading :database="$database" />
|
<livewire:project.database.heading :database="$database" />
|
||||||
<div class="pt-6">
|
<div>
|
||||||
<livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
|
<livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
|
||||||
<livewire:project.database.backup-executions :backup="$backup" />
|
<livewire:project.database.backup-executions :backup="$backup" />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -11,16 +11,16 @@
|
|||||||
class="flex overflow-x-scroll shrink-0 gap-6 items-center whitespace-nowrap sm:overflow-x-hidden scrollbar min-h-10">
|
class="flex overflow-x-scroll shrink-0 gap-6 items-center whitespace-nowrap sm:overflow-x-hidden scrollbar min-h-10">
|
||||||
<a class="{{ request()->routeIs('project.database.configuration') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.configuration') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.configuration', $parameters) }}">
|
href="{{ route('project.database.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
Configuration
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="{{ request()->routeIs('project.database.logs') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.logs') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.logs', $parameters) }}">
|
href="{{ route('project.database.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
Logs
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('project.database.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.command', $parameters) }}">
|
href="{{ route('project.database.command', $parameters) }}">
|
||||||
<button>Terminal</button>
|
Terminal
|
||||||
</a>
|
</a>
|
||||||
@if (
|
@if (
|
||||||
$database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
|
$database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||||
<a class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.backup.index', $parameters) }}">
|
href="{{ route('project.database.backup.index', $parameters) }}">
|
||||||
<button>Backups</button>
|
Backups
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</nav>
|
</nav>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
'If the database is currently in use data could be lost.',
|
'If the database is currently in use data could be lost.',
|
||||||
'All non-persistent data of this database (containers, networks, unused images) will be deleted (don\'t worry, no data is lost and you can start the database again).',
|
'All non-persistent data of this database (containers, networks, unused images) will be deleted (don\'t worry, no data is lost and you can start the database again).',
|
||||||
]" :confirmWithText="false" :confirmWithPassword="false"
|
]" :confirmWithText="false" :confirmWithPassword="false"
|
||||||
step1ButtonText="Continue" step2ButtonText="Stop Database">
|
step1ButtonText="Continue" step2ButtonText="Confirm">
|
||||||
<x-slot:button-title>
|
<x-slot:button-title>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
|
@@ -78,7 +78,7 @@
|
|||||||
<h2>Services</h2>
|
<h2>Services</h2>
|
||||||
<x-forms.button x-on:click="loadResources">Reload List</x-forms.button>
|
<x-forms.button x-on:click="loadResources">Reload List</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-4 text-xs">Trademarks Policy: The respective trademarks mentioned here are owned by
|
<div class="py-4 text-xs">Trademarks Policy: The respective trademarks mentioned here are owned by
|
||||||
the
|
the
|
||||||
respective
|
respective
|
||||||
companies, and use of them does not imply any affiliation or endorsement.<br>Find more services
|
companies, and use of them does not imply any affiliation or endorsement.<br>Find more services
|
||||||
|
@@ -72,6 +72,9 @@
|
|||||||
<template x-if="item.status.startsWith('exited')">
|
<template x-if="item.status.startsWith('exited')">
|
||||||
<div title="exited" class="bg-error badge-dashboard"></div>
|
<div title="exited" class="bg-error badge-dashboard"></div>
|
||||||
</template>
|
</template>
|
||||||
|
<template x-if="item.status.startsWith('starting')">
|
||||||
|
<div title="starting" class="bg-warning badge-dashboard"></div>
|
||||||
|
</template>
|
||||||
<template x-if="item.status.startsWith('restarting')">
|
<template x-if="item.status.startsWith('restarting')">
|
||||||
<div title="restarting" class="bg-warning badge-dashboard"></div>
|
<div title="restarting" class="bg-warning badge-dashboard"></div>
|
||||||
</template>
|
</template>
|
||||||
@@ -118,6 +121,9 @@
|
|||||||
<template x-if="item.status.startsWith('exited')">
|
<template x-if="item.status.startsWith('exited')">
|
||||||
<div title="exited" class="bg-error badge-dashboard"></div>
|
<div title="exited" class="bg-error badge-dashboard"></div>
|
||||||
</template>
|
</template>
|
||||||
|
<template x-if="item.status.startsWith('starting')">
|
||||||
|
<div title="starting" class="bg-warning badge-dashboard"></div>
|
||||||
|
</template>
|
||||||
<template x-if="item.status.startsWith('restarting')">
|
<template x-if="item.status.startsWith('restarting')">
|
||||||
<div title="restarting" class="bg-warning badge-dashboard"></div>
|
<div title="restarting" class="bg-warning badge-dashboard"></div>
|
||||||
</template>
|
</template>
|
||||||
@@ -164,6 +170,9 @@
|
|||||||
<template x-if="item.status.startsWith('exited')">
|
<template x-if="item.status.startsWith('exited')">
|
||||||
<div title="exited" class="bg-error badge-dashboard"></div>
|
<div title="exited" class="bg-error badge-dashboard"></div>
|
||||||
</template>
|
</template>
|
||||||
|
<template x-if="item.status.startsWith('starting')">
|
||||||
|
<div title="starting" class="bg-warning badge-dashboard"></div>
|
||||||
|
</template>
|
||||||
<template x-if="item.status.startsWith('restarting')">
|
<template x-if="item.status.startsWith('restarting')">
|
||||||
<div title="restarting" class="bg-warning badge-dashboard"></div>
|
<div title="restarting" class="bg-warning badge-dashboard"></div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
|
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
|
||||||
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
|
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
|
||||||
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Stop Service">
|
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Confirm">
|
||||||
<x-slot:button-title>
|
<x-slot:button-title>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
|
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
|
||||||
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
|
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
|
||||||
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Stop Service">
|
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Confirm">
|
||||||
<x-slot:button-title>
|
<x-slot:button-title>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
@else
|
@else
|
||||||
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
|
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
|
||||||
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
|
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
|
||||||
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Stop Service">
|
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Confirm">
|
||||||
<x-slot:button-title>
|
<x-slot:button-title>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
<script>
|
<script>
|
||||||
$wire.$on('stopEvent', () => {
|
$wire.$on('stopEvent', () => {
|
||||||
$wire.$dispatch('info',
|
$wire.$dispatch('info',
|
||||||
'Gracefully stopping service.<br/>It could take a while depending on the service.');
|
'Gracefully stopping service.<br/><br/>It could take a while depending on the service.');
|
||||||
$wire.$call('stop');
|
$wire.$call('stop');
|
||||||
});
|
});
|
||||||
$wire.$on('startEvent', async () => {
|
$wire.$on('startEvent', async () => {
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$wire.$dispatch('info',
|
$wire.$dispatch('info',
|
||||||
'Gracefully stopping service.<br/>It could take a while depending on the service.');
|
'Gracefully stopping service.<br/><br/>It could take a while depending on the service.');
|
||||||
window.dispatchEvent(new CustomEvent('startservice'));
|
window.dispatchEvent(new CustomEvent('startservice'));
|
||||||
$wire.$call('restart');
|
$wire.$call('restart');
|
||||||
});
|
});
|
||||||
|
@@ -39,10 +39,15 @@
|
|||||||
label="Image" id="application.image"></x-forms.input>
|
label="Image" id="application.image"></x-forms.input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="pt-2">Advanced</h3>
|
<h3 class="py-2 pt-4">Advanced</h3>
|
||||||
<div class="w-96">
|
<div class="w-96 flex flex-col gap-1">
|
||||||
<x-forms.checkbox instantSave id="application.is_gzip_enabled" label="Enable gzip compression"
|
@if (str($application->image)->contains('pocketbase'))
|
||||||
|
<x-forms.checkbox instantSave id="application.is_gzip_enabled" label="Enable Gzip Compression"
|
||||||
|
helper="Pocketbase does not need gzip compression, otherwise SSE will not work." disabled />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox instantSave id="application.is_gzip_enabled" label="Enable Gzip Compression"
|
||||||
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this." />
|
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this." />
|
||||||
|
@endif
|
||||||
<x-forms.checkbox instantSave id="application.is_stripprefix_enabled" label="Strip Prefixes"
|
<x-forms.checkbox instantSave id="application.is_stripprefix_enabled" label="Strip Prefixes"
|
||||||
helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api." />
|
helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api." />
|
||||||
<x-forms.checkbox instantSave label="Exclude from service status"
|
<x-forms.checkbox instantSave label="Exclude from service status"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<form wire:submit='submit' @class([
|
<form wire:submit='submit' @class([
|
||||||
'flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base',
|
'flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base',
|
||||||
'border-error' => $is_really_required,
|
'border-error' => $is_really_required,
|
||||||
'dark:border-coolgray-300' => !$is_really_required,
|
'dark:border-coolgray-300 border-neutral-200' => !$is_really_required,
|
||||||
])>
|
])>
|
||||||
@if ($isLocked)
|
@if ($isLocked)
|
||||||
<div class="flex flex-1 w-full gap-2">
|
<div class="flex flex-1 w-full gap-2">
|
||||||
|
@@ -20,7 +20,6 @@
|
|||||||
@if (count($containers) === 0)
|
@if (count($containers) === 0)
|
||||||
<div>No containers are running or terminal access is disabled on this server.</div>
|
<div>No containers are running or terminal access is disabled on this server.</div>
|
||||||
@else
|
@else
|
||||||
@foreach ($containers as $container)
|
|
||||||
<form class="w-full flex gap-2 items-end" wire:submit="$dispatchSelf('connectToContainer')">
|
<form class="w-full flex gap-2 items-end" wire:submit="$dispatchSelf('connectToContainer')">
|
||||||
<x-forms.select label="Container" id="container" required wire:model="selected_container">
|
<x-forms.select label="Container" id="container" required wire:model="selected_container">
|
||||||
@foreach ($containers as $container)
|
@foreach ($containers as $container)
|
||||||
@@ -36,7 +35,6 @@
|
|||||||
<x-forms.button :disabled="$isConnecting"
|
<x-forms.button :disabled="$isConnecting"
|
||||||
type="submit">{{ $isConnecting ? 'Connecting...' : 'Connect' }}</x-forms.button>
|
type="submit">{{ $isConnecting ? 'Connecting...' : 'Connect' }}</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
@endforeach
|
|
||||||
<div class="mx-auto w-full">
|
<div class="mx-auto w-full">
|
||||||
<livewire:project.shared.terminal />
|
<livewire:project.shared.terminal />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="p-4 my-4 border dark:border-coolgray-200">
|
<div class="p-4 my-4 border dark:border-coolgray-200 border-neutral-200">
|
||||||
<div x-init="$wire.getLogs" id="screen" x-data="{
|
<div x-init="$wire.getLogs" id="screen" x-data="{
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
alwaysScroll: false,
|
alwaysScroll: false,
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
|
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
|
||||||
</form>
|
</form>
|
||||||
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
|
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
|
||||||
<div class="flex overflow-y-auto flex-col-reverse px-4 py-2 w-full bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
|
<div class="flex overflow-y-auto flex-col-reverse px-4 py-2 w-full bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300 border-neutral-200"
|
||||||
:class="fullscreen ? '' : 'max-h-96 border border-solid rounded-sm'">
|
:class="fullscreen ? '' : 'max-h-96 border border-solid rounded-sm'">
|
||||||
<div :class="fullscreen ? 'fixed top-4 right-4' : 'absolute top-6 right-0'">
|
<div :class="fullscreen ? 'fixed top-4 right-4' : 'absolute top-6 right-0'">
|
||||||
<div class="flex justify-end gap-4" :class="fullscreen ? 'fixed' : ''"
|
<div class="flex justify-end gap-4" :class="fullscreen ? 'fixed' : ''"
|
||||||
|
@@ -6,7 +6,8 @@
|
|||||||
<div class="pb-4">Define how your resource's health should be checked.</div>
|
<div class="pb-4">Define how your resource's health should be checked.</div>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
@if ($resource->custom_healthcheck_found)
|
@if ($resource->custom_healthcheck_found)
|
||||||
<div class="text-warning">A custom health check has been found and will be used until you enable this.</div>
|
<div class="dark:text-warning">A custom health check has been found and will be used until you enable this.
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
<x-forms.checkbox instantSave id="resource.health_check_enabled" label="Enabled" />
|
<x-forms.checkbox instantSave id="resource.health_check_enabled" label="Enabled" />
|
||||||
@@ -25,8 +26,8 @@
|
|||||||
<x-forms.input id="resource.health_check_response_text" placeholder="OK" label="Response Text" />
|
<x-forms.input id="resource.health_check_response_text" placeholder="OK" label="Response Text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input min=1 type="number" id="resource.health_check_interval" placeholder="30" label="Interval (s)"
|
<x-forms.input min="1" type="number" id="resource.health_check_interval" placeholder="30"
|
||||||
required />
|
label="Interval (s)" required />
|
||||||
<x-forms.input type="number" id="resource.health_check_timeout" placeholder="30" label="Timeout (s)"
|
<x-forms.input type="number" id="resource.health_check_timeout" placeholder="30" label="Timeout (s)"
|
||||||
required />
|
required />
|
||||||
<x-forms.input type="number" id="resource.health_check_retries" placeholder="3" label="Retries" required />
|
<x-forms.input type="number" id="resource.health_check_retries" placeholder="3" label="Retries" required />
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2>Logs</h2>
|
<h2>Logs</h2>
|
||||||
@if (str($status)->contains('exited'))
|
@if (str($status)->contains('exited'))
|
||||||
<div class="pt-2">The resource is not running.</div>
|
<div class="pt-4">The resource is not running.</div>
|
||||||
@else
|
@else
|
||||||
<div class="pt-2" wire:loading wire:target="loadAllContainers">
|
<div class="pt-2" wire:loading wire:target="loadAllContainers">
|
||||||
Loading containers...
|
Loading containers...
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2>Logs</h2>
|
<h2>Logs</h2>
|
||||||
@if (str($status)->contains('exited'))
|
@if (str($status)->contains('exited'))
|
||||||
<div class="pt-2">The resource is not running.</div>
|
<div class="pt-4">The resource is not running.</div>
|
||||||
@else
|
@else
|
||||||
<div class="pt-2" wire:loading wire:target="loadAllContainers">
|
<div class="pt-2" wire:loading wire:target="loadAllContainers">
|
||||||
Loading containers...
|
Loading containers...
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2>Logs</h2>
|
<h2>Logs</h2>
|
||||||
@if (str($status)->contains('exited'))
|
@if (str($status)->contains('exited'))
|
||||||
<div class="pt-2">The resource is not running.</div>
|
<div class="pt-4">The resource is not running.</div>
|
||||||
@else
|
@else
|
||||||
<div class="pt-2" wire:loading wire:target="loadAllContainers">
|
<div class="pt-2" wire:loading wire:target="loadAllContainers">
|
||||||
Loading containers...
|
Loading containers...
|
||||||
|
@@ -9,14 +9,13 @@
|
|||||||
<div>API is disabled. If you want to use the API, please enable it in the <a
|
<div>API is disabled. If you want to use the API, please enable it in the <a
|
||||||
href="{{ route('settings.index') }}" class="underline dark:text-white">Settings</a> menu.</div>
|
href="{{ route('settings.index') }}" class="underline dark:text-white">Settings</a> menu.</div>
|
||||||
@else
|
@else
|
||||||
<div>Tokens are created with the current team as scope. You will only have access to this team's resources.
|
<div>Tokens are created with the current team as scope.</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<h3>New Token</h3>
|
<h3>New Token</h3>
|
||||||
<form class="flex flex-col gap-2 pt-4" wire:submit='addNewToken'>
|
<form class="flex flex-col gap-2" wire:submit='addNewToken'>
|
||||||
<div class="flex gap-2 items-end">
|
<div class="flex gap-2 items-end w-96">
|
||||||
<x-forms.input required id="description" label="Description" />
|
<x-forms.input required id="description" label="Description" />
|
||||||
<x-forms.button type="submit">Create New Token</x-forms.button>
|
<x-forms.button type="submit">Create</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
Permissions
|
Permissions
|
||||||
@@ -37,18 +36,18 @@
|
|||||||
helper="Root access, be careful!" :checked="in_array('root', $permissions)"></x-forms.checkbox>
|
helper="Root access, be careful!" :checked="in_array('root', $permissions)"></x-forms.checkbox>
|
||||||
@if (!in_array('root', $permissions))
|
@if (!in_array('root', $permissions))
|
||||||
<x-forms.checkbox label="write" wire:model.live="permissions" domValue="write"
|
<x-forms.checkbox label="write" wire:model.live="permissions" domValue="write"
|
||||||
helper="Write access to all resources" :checked="in_array('write', $permissions)"></x-forms.checkbox>
|
helper="Write access to all resources." :checked="in_array('write', $permissions)"></x-forms.checkbox>
|
||||||
<x-forms.checkbox label="deploy" wire:model.live="permissions" domValue="deploy"
|
<x-forms.checkbox label="deploy" wire:model.live="permissions" domValue="deploy"
|
||||||
helper="Can trigger deploy webhooks" :checked="in_array('deploy', $permissions)"></x-forms.checkbox>
|
helper="Can trigger deploy webhooks." :checked="in_array('deploy', $permissions)"></x-forms.checkbox>
|
||||||
<x-forms.checkbox label="read" domValue="read" wire:model.live="permissions" domValue="read"
|
<x-forms.checkbox label="read" domValue="read" wire:model.live="permissions" domValue="read"
|
||||||
:checked="in_array('read', $permissions)"></x-forms.checkbox>
|
:checked="in_array('read', $permissions)"></x-forms.checkbox>
|
||||||
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" domValue="read:sensitive"
|
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" domValue="read:sensitive"
|
||||||
helper="Responses will include secrets, logs, passwords, and compose file contents"
|
helper="Responses will include secrets, logs, passwords, and compose file contents."
|
||||||
:checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
|
:checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if (in_array('root', $permissions))
|
@if (in_array('root', $permissions))
|
||||||
<div class="font-bold text-warning">Root access, be careful!</div>
|
<div class="font-bold dark:text-warning">Root access, be careful!</div>
|
||||||
@endif
|
@endif
|
||||||
</form>
|
</form>
|
||||||
@if (session()->has('token'))
|
@if (session()->has('token'))
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>Sends service logs to 3rd party tools.</div>
|
<div>Sends service logs to 3rd party tools.</div>
|
||||||
<div class="flex flex-col gap-4 pt-4">
|
<div class="flex flex-col gap-4 pt-4">
|
||||||
<div class="p-4 border dark:border-coolgray-300">
|
<div class="p-4 border dark:border-coolgray-300 border-neutral-200">
|
||||||
<form wire:submit='submit("newrelic")' class="flex flex-col">
|
<form wire:submit='submit("newrelic")' class="flex flex-col">
|
||||||
<h3>New Relic</h3>
|
<h3>New Relic</h3>
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
@endif
|
@endif
|
||||||
<div wire:loading wire:target="checkProxy" class="badge badge-warning"></div>
|
<div wire:loading wire:target="checkProxy" class="badge badge-warning"></div>
|
||||||
<div wire:loading wire:target="checkProxy"
|
<div wire:loading wire:target="checkProxy"
|
||||||
class="pl-2 pr-1 text-xs font-bold tracking-wider text-warning">
|
class="pl-2 pr-1 text-xs font-bold tracking-wider dark:text-warning">
|
||||||
Checking Ports Availability...
|
Checking Ports Availability...
|
||||||
</div>
|
</div>
|
||||||
@if ($proxyStatus !== 'exited')
|
@if ($proxyStatus !== 'exited')
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
href="{{ route('server.show', [
|
href="{{ route('server.show', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Configuration</button>
|
Configuration
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
|
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
|
||||||
@@ -68,26 +68,26 @@
|
|||||||
href="{{ route('server.proxy', [
|
href="{{ route('server.proxy', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Proxy</button>
|
Proxy
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.resources', [
|
href="{{ route('server.resources', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Resources</button>
|
Resources
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.command', [
|
href="{{ route('server.command', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Terminal</button>
|
Terminal
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.security.patches') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.security.patches') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.security.patches', [
|
href="{{ route('server.security.patches', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Security</button>
|
Security
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="order-first sm:order-last">
|
<div class="order-first sm:order-last">
|
||||||
|
@@ -27,7 +27,9 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
</x-forms.select>
|
</x-forms.select>
|
||||||
<div class="">
|
<div class="">
|
||||||
<x-forms.checkbox instantSave type="checkbox" id="is_build_server" label="Use it as a build server?" />
|
<x-forms.checkbox instantSave type="checkbox" id="is_build_server"
|
||||||
|
helper="Build servers are used to build your applications, so you cannot deploy applications to it."
|
||||||
|
label="Use it as a build server?" />
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3>
|
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3>
|
||||||
|
@@ -13,8 +13,8 @@
|
|||||||
<div>Here you can find all resources that are managed by Coolify.</div>
|
<div>Here you can find all resources that are managed by Coolify.</div>
|
||||||
<div class="flex flex-row gap-4 py-10">
|
<div class="flex flex-row gap-4 py-10">
|
||||||
<div @class([
|
<div @class([
|
||||||
'box-without-bg cursor-pointer bg-coolgray-100 text-white w-full text-center items-center justify-center',
|
'box-without-bg cursor-pointer dark:bg-coolgray-100 dark:text-white w-full text-center items-center justify-center',
|
||||||
'bg-coollabs' => $activeTab === 'managed',
|
'dark:bg-coollabs bg-coollabs text-white' => $activeTab === 'managed',
|
||||||
]) wire:click="loadManagedContainers">
|
]) wire:click="loadManagedContainers">
|
||||||
Managed
|
Managed
|
||||||
<div class="flex flex-col items-center justify-center">
|
<div class="flex flex-col items-center justify-center">
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div @class([
|
<div @class([
|
||||||
'box-without-bg cursor-pointer bg-coolgray-100 text-white w-full text-center items-center justify-center',
|
'box-without-bg cursor-pointer dark:bg-coolgray-100 dark:text-white w-full text-center items-center justify-center',
|
||||||
'bg-coollabs' => $activeTab === 'unmanaged',
|
'dark:bg-coollabs bg-coollabs text-white' => $activeTab === 'unmanaged',
|
||||||
]) wire:click="loadUnmanagedContainers">
|
]) wire:click="loadUnmanagedContainers">
|
||||||
Unmanaged
|
Unmanaged
|
||||||
<div class="flex flex-col items-center justify-center">
|
<div class="flex flex-col items-center justify-center">
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
<x-settings.navbar />
|
<x-settings.navbar />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2 pb-2">
|
||||||
<h2>Backup</h2>
|
<h2>Backup</h2>
|
||||||
@if (isset($database) && $server->isFunctional())
|
@if (isset($database) && $server->isFunctional())
|
||||||
<x-forms.button type="submit" wire:click="submit">
|
<x-forms.button type="submit" wire:click="submit">
|
||||||
|
@@ -22,13 +22,13 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-4">Instance wide email settings for password resets, invitations, etc.</div>
|
<div class="pb-4">Instance wide email settings for password resets, invitations, etc.</div>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-2">
|
||||||
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
|
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
|
||||||
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
|
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="p-4 border dark:border-coolgray-300">
|
<div class="p-4 border dark:border-coolgray-300 border-neutral-200">
|
||||||
<form wire:submit.prevent="submitSmtp" class="flex flex-col">
|
<form wire:submit.prevent="submitSmtp" class="flex flex-col">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<h3>SMTP Server</h3>
|
<h3>SMTP Server</h3>
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 border dark:border-coolgray-300">
|
<div class="p-4 border dark:border-coolgray-300 border-neutral-200">
|
||||||
<form wire:submit.prevent="submitResend" class="flex flex-col">
|
<form wire:submit.prevent="submitResend" class="flex flex-col">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<h3>Resend</h3>
|
<h3>Resend</h3>
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<x-settings.navbar />
|
<x-settings.navbar />
|
||||||
<form wire:submit='submit' class="flex flex-col">
|
<form wire:submit='submit' class="flex flex-col">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2 pb-2">
|
||||||
<h2>Authentication</h2>
|
<h2>Authentication</h2>
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 pt-4">
|
<div class="flex flex-col gap-2 pt-4">
|
||||||
@foreach ($oauth_settings_map as $oauth_setting)
|
@foreach ($oauth_settings_map as $oauth_setting)
|
||||||
<div class="p-4 border dark:border-coolgray-300">
|
<div class="p-4 border dark:border-coolgray-300 border-neutral-200">
|
||||||
<h3>{{ ucfirst($oauth_setting->provider) }}</h3>
|
<h3>{{ ucfirst($oauth_setting->provider) }}</h3>
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
<x-forms.checkbox instantSave="instantSave('{{ $oauth_setting->provider }}')"
|
<x-forms.checkbox instantSave="instantSave('{{ $oauth_setting->provider }}')"
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
label="Client ID" />
|
label="Client ID" />
|
||||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_secret"
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_secret"
|
||||||
type="password" label="Client Secret" autocomplete="new-password" />
|
type="password" label="Client Secret" autocomplete="new-password" />
|
||||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.redirect_uri" placeholder="{{ route('auth.callback', $oauth_setting->provider) }}"
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.redirect_uri"
|
||||||
label="Redirect URI" />
|
placeholder="{{ route('auth.callback', $oauth_setting->provider) }}" label="Redirect URI" />
|
||||||
@if ($oauth_setting->provider == 'azure')
|
@if ($oauth_setting->provider == 'azure')
|
||||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.tenant"
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.tenant"
|
||||||
label="Tenant" />
|
label="Tenant" />
|
||||||
@@ -37,7 +37,10 @@
|
|||||||
helper="Optional parameter that supplies a hosted domain (HD) to Google, which<br>triggers a login hint to be displayed on the OAuth screen with this domain.<br><br><a class='underline dark:text-warning text-coollabs' href='https://developers.google.com/identity/openid-connect/openid-connect#hd-param' target='_blank'>Google Documentation</a>"
|
helper="Optional parameter that supplies a hosted domain (HD) to Google, which<br>triggers a login hint to be displayed on the OAuth screen with this domain.<br><br><a class='underline dark:text-warning text-coollabs' href='https://developers.google.com/identity/openid-connect/openid-connect#hd-param' target='_blank'>Google Documentation</a>"
|
||||||
label="Tenant" />
|
label="Tenant" />
|
||||||
@endif
|
@endif
|
||||||
@if ($oauth_setting->provider == 'authentik')
|
@if (
|
||||||
|
$oauth_setting->provider == 'authentik' ||
|
||||||
|
$oauth_setting->provider == 'clerk' ||
|
||||||
|
$oauth_setting->provider == 'zitadel')
|
||||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.base_url"
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.base_url"
|
||||||
label="Base URL" />
|
label="Base URL" />
|
||||||
@endif
|
@endif
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
<x-settings.navbar />
|
<x-settings.navbar />
|
||||||
<form wire:submit='submit' class="flex flex-col">
|
<form wire:submit='submit' class="flex flex-col">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2 pb-2">
|
||||||
<h2>Configuration</h2>
|
<h2>Configuration</h2>
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
|
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
|
||||||
placeholder="1.1.1.1,8.8.8.8" />
|
placeholder="1.1.1.1,8.8.8.8" />
|
||||||
<h4 class="pt-6">Update</h4>
|
<h4 class="pt-6">Update</h4>
|
||||||
<div class="text-right md:w-96">
|
<div class="text-right md:w-96 pb-4">
|
||||||
@if (!is_null(config('constants.coolify.autoupdate', null)))
|
@if (!is_null(config('constants.coolify.autoupdate', null)))
|
||||||
<div class="text-right md:w-96">
|
<div class="text-right md:w-96">
|
||||||
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there."
|
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there."
|
||||||
@@ -128,6 +128,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="py-4">Confirmation Settings</h4>
|
<h4 class="py-4">Confirmation Settings</h4>
|
||||||
|
<div class="md:w-96 ">
|
||||||
|
<x-forms.checkbox instantSave id="is_sponsorship_popup_enabled" label="Show Sponsorship Popup"
|
||||||
|
helper="When enabled, sponsorship popups will be shown monthly to users. When disabled, the sponsorship popup will be permanently hidden for all users." />
|
||||||
|
</div>
|
||||||
|
|
||||||
@if ($disable_two_step_confirmation)
|
@if ($disable_two_step_confirmation)
|
||||||
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
|
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
|
||||||
|
@@ -56,7 +56,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div class="flex items-center pt-6">
|
<div class="flex items-center pt-6">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="flex-none w-8 h-8 mr-3 text-warning"
|
<svg xmlns="http://www.w3.org/2000/svg" class="flex-none w-8 h-8 mr-3 dark:text-warning"
|
||||||
fill="currentColor" viewBox="0 0 256 256">
|
fill="currentColor" viewBox="0 0 256 256">
|
||||||
<path
|
<path
|
||||||
d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z">
|
d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z">
|
||||||
@@ -89,8 +89,8 @@
|
|||||||
</div>
|
</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 dark:text-warning" viewBox="0 0 20 20"
|
||||||
aria-hidden="true">
|
fill="currentColor" aria-hidden="true">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||||
clip-rule="evenodd" />
|
clip-rule="evenodd" />
|
||||||
@@ -99,8 +99,8 @@
|
|||||||
<span class="px-1 font-bold dark:text-white">unlimited</span> servers
|
<span class="px-1 font-bold dark:text-white">unlimited</span> servers
|
||||||
</li>
|
</li>
|
||||||
<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 dark:text-warning" viewBox="0 0 20 20"
|
||||||
aria-hidden="true">
|
fill="currentColor" aria-hidden="true">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||||
clip-rule="evenodd" />
|
clip-rule="evenodd" />
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
<span class="px-1 font-bold dark:text-white">unlimited</span> applications per server
|
<span class="px-1 font-bold dark:text-white">unlimited</span> applications per server
|
||||||
</li>
|
</li>
|
||||||
<li class="flex gap-x-3">
|
<li class="flex gap-x-3">
|
||||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
<svg class="flex-none w-5 h-6 dark:text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
Free email notifications
|
Free email notifications
|
||||||
</li>
|
</li>
|
||||||
<li class="flex gap-x-3">
|
<li class="flex gap-x-3">
|
||||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
<svg class="flex-none w-5 h-6 dark:text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex items-start gap-2 pb-10">
|
<div class="flex items-start gap-2 pb-10">
|
||||||
<div>
|
<div>
|
||||||
<h1>Tags</h1>
|
<h1 class="pb-2">Tags</h1>
|
||||||
<div>Tags help you to perform actions on multiple resources.</div>
|
<div>Tags help you to perform actions on multiple resources.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
<div class="flex items-center justify-center gap-2 mx-4 text-xs font-bold ">
|
<div class="flex items-center justify-center gap-2 mx-4 text-xs font-bold ">
|
||||||
<x-modal-confirmation title="Confirm User Deletion?" buttonTitle="Delete" isErrorButton
|
<x-modal-confirmation title="Confirm User Deletion?" buttonTitle="Delete" isErrorButton
|
||||||
submitAction="delete({{ $user->id }})" :actions="[
|
submitAction="delete({{ $user->id }})" :actions="[
|
||||||
'The selected user will be permanently deleted from Coolify and the database.',
|
'The selected user will be permanently deleted from Coolify\'s database.',
|
||||||
'All resources (application, databases, services, configurations, servers, private keys, tags, etc.) related to this user will be deleted from Coolify and from the server (if the server is reachable).',
|
'All resources (application, databases, services, configurations, servers, private keys, tags, etc.) related to this user\'s default team will be deleted from Coolify\'s database.',
|
||||||
]"
|
]"
|
||||||
confirmationText="{{ $user->name }}"
|
confirmationText="{{ $user->name }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the User Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the User Name below"
|
||||||
|
@@ -27,8 +27,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<template x-teleport="body">
|
<template x-teleport="body">
|
||||||
<div x-show="modalOpen"
|
<div x-show="modalOpen"
|
||||||
class="fixed top-0 lg:pt-10 left-0 z-99 flex items-start justify-center w-screen h-screen"
|
class="fixed top-0 lg:pt-10 left-0 z-99 flex items-start justify-center w-screen h-screen" x-cloak>
|
||||||
x-cloak>
|
|
||||||
<div x-show="modalOpen" x-transition:enter="ease-out duration-100"
|
<div x-show="modalOpen" x-transition:enter="ease-out duration-100"
|
||||||
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
||||||
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
|
x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100"
|
||||||
@@ -55,8 +54,11 @@
|
|||||||
<p>Are you sure you would like to upgrade your instance to {{ $latestVersion }}?</p>
|
<p>Are you sure you would like to upgrade your instance to {{ $latestVersion }}?</p>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<div class="p-4 mb-4 text-yellow-800 border border-yellow-300 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300 dark:border-yellow-800">
|
<div
|
||||||
<p class="font-medium">Warning: Any deployments running during the update process will fail. Please ensure no deployments are in progress on any server before continuing.</p>
|
class="p-4 mb-4 text-yellow-800 border border-yellow-300 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300 dark:border-yellow-800">
|
||||||
|
<p class="font-medium">Warning: Any deployments running during the update process will
|
||||||
|
fail. Please ensure no deployments are in progress on any server before continuing.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>You can review the changelogs <a class="font-bold underline dark:text-white"
|
<p>You can review the changelogs <a class="font-bold underline dark:text-white"
|
||||||
href="https://github.com/coollabsio/coolify/releases" target="_blank">here</a>.</p>
|
href="https://github.com/coollabsio/coolify/releases" target="_blank">here</a>.</p>
|
||||||
|
@@ -58,7 +58,7 @@ Route::group([
|
|||||||
Route::patch('/security/keys/{uuid}', [SecurityController::class, 'update_key'])->middleware(['api.ability:write']);
|
Route::patch('/security/keys/{uuid}', [SecurityController::class, 'update_key'])->middleware(['api.ability:write']);
|
||||||
Route::delete('/security/keys/{uuid}', [SecurityController::class, 'delete_key'])->middleware(['api.ability:write']);
|
Route::delete('/security/keys/{uuid}', [SecurityController::class, 'delete_key'])->middleware(['api.ability:write']);
|
||||||
|
|
||||||
Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy'])->middleware(['api.ability:write,deploy']);
|
Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy'])->middleware(['api.ability:deploy']);
|
||||||
Route::get('/deployments', [DeployController::class, 'deployments'])->middleware(['api.ability:read']);
|
Route::get('/deployments', [DeployController::class, 'deployments'])->middleware(['api.ability:read']);
|
||||||
Route::get('/deployments/{uuid}', [DeployController::class, 'deployment_by_uuid'])->middleware(['api.ability:read']);
|
Route::get('/deployments/{uuid}', [DeployController::class, 'deployment_by_uuid'])->middleware(['api.ability:read']);
|
||||||
Route::get('/deployments/applications/{uuid}', [DeployController::class, 'get_application_deployments'])->middleware(['api.ability:read']);
|
Route::get('/deployments/applications/{uuid}', [DeployController::class, 'get_application_deployments'])->middleware(['api.ability:read']);
|
||||||
|
@@ -164,7 +164,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
|
|
||||||
Route::prefix('invitations')->group(function () {
|
Route::prefix('invitations')->group(function () {
|
||||||
Route::get('/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept');
|
Route::get('/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept');
|
||||||
Route::get('/{uuid}/revoke', [Controller::class, 'revoke_invitation'])->name('team.invitation.revoke');
|
Route::get('/{uuid}/revoke', [Controller::class, 'revokeInvitation'])->name('team.invitation.revoke');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::get('/projects', ProjectIndex::class)->name('project.index');
|
Route::get('/projects', ProjectIndex::class)->name('project.index');
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user