diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index c11930538..c6a5b3273 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -22,8 +22,6 @@ You can ask for guidance anytime on our - Run `spin up` - You can notice that errors will be thrown. Don't worry. - If you see weird permission errors, especially on Mac, run `sudo spin up` instead. -- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database. - ### 4) Start development You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`. @@ -31,7 +29,6 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y Mails are caught by Mailpit: `localhost:8025` - ## New Service Contribution Check out the docs [here](https://coolify.io/docs/how-to-add-a-service). diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php index 6fd74025c..4c2c57f60 100644 --- a/app/Actions/Server/UpdateCoolify.php +++ b/app/Actions/Server/UpdateCoolify.php @@ -18,7 +18,7 @@ class UpdateCoolify try { $settings = InstanceSettings::get(); ray('Running InstanceAutoUpdateJob'); - $this->server = Server::find(0)->first(); + $this->server = Server::find(0); if (!$this->server) { return; } diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index dc722aa83..a90b368ab 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -11,6 +11,7 @@ class StartService use AsAction; public function handle(Service $service) { + ray('Starting service: ' . $service->name); $network = $service->destination->network; $service->saveComposeConfigs(); $commands[] = "cd " . $service->workdir(); diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index f7de50165..d9ac6376e 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -10,6 +10,7 @@ class StopService use AsAction; public function handle(Service $service) { + ray('Stopping service: ' . $service->name); $applications = $service->applications()->get(); foreach ($applications as $application) { instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server); diff --git a/app/Actions/Shared/PullImage.php b/app/Actions/Shared/PullImage.php new file mode 100644 index 000000000..42713b227 --- /dev/null +++ b/app/Actions/Shared/PullImage.php @@ -0,0 +1,25 @@ +saveComposeConfigs(); + + $commands[] = "cd " . $resource->workdir(); + $commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'"; + $commands[] = "docker compose pull"; + + $server = data_get($resource, 'server'); + + if (!$server) return; + + instant_remote_process($commands, $resource->server); + } +} diff --git a/app/Console/Commands/Dev.php b/app/Console/Commands/Dev.php new file mode 100644 index 000000000..ba60826d1 --- /dev/null +++ b/app/Console/Commands/Dev.php @@ -0,0 +1,33 @@ +get(); + $halted_deployments = ApplicationDeploymentQueue::where('status', '==', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', '==', ApplicationDeploymentStatus::QUEUED)->get(); foreach ($halted_deployments as $deployment) { $deployment->status = ApplicationDeploymentStatus::FAILED->value; $deployment->save(); @@ -115,16 +115,19 @@ class Init extends Command $applications = Application::all(); foreach ($applications as $application) { if (!data_get($application, 'environment')) { - echo 'Application without environment' . $application->name . 'deleting\n'; + echo 'Application without environment: ' . $application->name . ' soft deleting\n'; $application->delete(); + continue; } if (!$application->destination()) { - echo 'Application without destination' . $application->name . 'deleting\n'; + echo 'Application without destination: ' . $application->name . ' soft deleting\n'; $application->delete(); + continue; } if (!data_get($application, 'destination.server')) { - echo 'Application without server' . $application->name . 'deleting\n'; + echo 'Application without server: ' . $application->name . ' soft deleting\n'; $application->delete(); + continue; } } } catch (\Throwable $e) { @@ -134,16 +137,19 @@ class Init extends Command $postgresqls = StandalonePostgresql::all(); foreach ($postgresqls as $postgresql) { if (!data_get($postgresql, 'environment')) { - echo 'Postgresql without environment' . $postgresql->name . 'deleting\n'; + echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n'; $postgresql->delete(); + continue; } if (!$postgresql->destination()) { - echo 'Postgresql without destination' . $postgresql->name . 'deleting\n'; + echo 'Postgresql without destination: ' . $postgresql->name . ' soft deleting\n'; $postgresql->delete(); + continue; } if (!data_get($postgresql, 'destination.server')) { - echo 'Postgresql without server' . $postgresql->name . 'deleting\n'; + echo 'Postgresql without server: ' . $postgresql->name . ' soft deleting\n'; $postgresql->delete(); + continue; } } } catch (\Throwable $e) { @@ -153,16 +159,19 @@ class Init extends Command $redis = StandaloneRedis::all(); foreach ($redis as $redis) { if (!data_get($redis, 'environment')) { - echo 'Redis without environment' . $redis->name . 'deleting\n'; + echo 'Redis without environment: ' . $redis->name . ' soft deleting\n'; $redis->delete(); + continue; } if (!$redis->destination()) { - echo 'Redis without destination' . $redis->name . 'deleting\n'; + echo 'Redis without destination: ' . $redis->name . ' soft deleting\n'; $redis->delete(); + continue; } if (!data_get($redis, 'destination.server')) { - echo 'Redis without server' . $redis->name . 'deleting\n'; + echo 'Redis without server: ' . $redis->name . ' soft deleting\n'; $redis->delete(); + continue; } } } catch (\Throwable $e) { @@ -173,16 +182,19 @@ class Init extends Command $mongodbs = StandaloneMongodb::all(); foreach ($mongodbs as $mongodb) { if (!data_get($mongodb, 'environment')) { - echo 'Mongodb without environment' . $mongodb->name . 'deleting\n'; + echo 'Mongodb without environment: ' . $mongodb->name . ' soft deleting\n'; $mongodb->delete(); + continue; } if (!$mongodb->destination()) { - echo 'Mongodb without destination' . $mongodb->name . 'deleting\n'; + echo 'Mongodb without destination: ' . $mongodb->name . ' soft deleting\n'; $mongodb->delete(); + continue; } if (!data_get($mongodb, 'destination.server')) { - echo 'Mongodb without server' . $mongodb->name . 'deleting\n'; + echo 'Mongodb without server: ' . $mongodb->name . ' soft deleting\n'; $mongodb->delete(); + continue; } } } catch (\Throwable $e) { @@ -193,16 +205,19 @@ class Init extends Command $mysqls = StandaloneMysql::all(); foreach ($mysqls as $mysql) { if (!data_get($mysql, 'environment')) { - echo 'Mysql without environment' . $mysql->name . 'deleting\n'; + echo 'Mysql without environment: ' . $mysql->name . ' soft deleting\n'; $mysql->delete(); + continue; } if (!$mysql->destination()) { - echo 'Mysql without destination' . $mysql->name . 'deleting\n'; + echo 'Mysql without destination: ' . $mysql->name . ' soft deleting\n'; $mysql->delete(); + continue; } if (!data_get($mysql, 'destination.server')) { - echo 'Mysql without server' . $mysql->name . 'deleting\n'; + echo 'Mysql without server: ' . $mysql->name . ' soft deleting\n'; $mysql->delete(); + continue; } } } catch (\Throwable $e) { @@ -213,16 +228,19 @@ class Init extends Command $mariadbs = StandaloneMariadb::all(); foreach ($mariadbs as $mariadb) { if (!data_get($mariadb, 'environment')) { - echo 'Mariadb without environment' . $mariadb->name . 'deleting\n'; + echo 'Mariadb without environment: ' . $mariadb->name . ' soft deleting\n'; $mariadb->delete(); + continue; } if (!$mariadb->destination()) { - echo 'Mariadb without destination' . $mariadb->name . 'deleting\n'; + echo 'Mariadb without destination: ' . $mariadb->name . ' soft deleting\n'; $mariadb->delete(); + continue; } if (!data_get($mariadb, 'destination.server')) { - echo 'Mariadb without server' . $mariadb->name . 'deleting\n'; + echo 'Mariadb without server: ' . $mariadb->name . ' soft deleting\n'; $mariadb->delete(); + continue; } } } catch (\Throwable $e) { @@ -233,16 +251,19 @@ class Init extends Command $services = Service::all(); foreach ($services as $service) { if (!data_get($service, 'environment')) { - echo 'Service without environment' . $service->name . 'deleting\n'; + echo 'Service without environment: ' . $service->name . ' soft deleting\n'; $service->delete(); + continue; } if (!$service->destination()) { - echo 'Service without destination' . $service->name . 'deleting\n'; + echo 'Service without destination: ' . $service->name . ' soft deleting\n'; $service->delete(); + continue; } if (!data_get($service, 'server')) { - echo 'Service without server' . $service->name . 'deleting\n'; + echo 'Service without server: ' . $service->name . ' soft deleting\n'; $service->delete(); + continue; } } } catch (\Throwable $e) { @@ -252,8 +273,9 @@ class Init extends Command $serviceApplications = ServiceApplication::all(); foreach ($serviceApplications as $service) { if (!data_get($service, 'service')) { - echo 'ServiceApplication without service' . $service->name . 'deleting\n'; + echo 'ServiceApplication without service: ' . $service->name . ' soft deleting\n'; $service->delete(); + continue; } } } catch (\Throwable $e) { @@ -263,8 +285,9 @@ class Init extends Command $serviceDatabases = ServiceDatabase::all(); foreach ($serviceDatabases as $service) { if (!data_get($service, 'service')) { - echo 'ServiceDatabase without service' . $service->name . 'deleting\n'; + echo 'ServiceDatabase without service: ' . $service->name . ' soft deleting\n'; $service->delete(); + continue; } } } catch (\Throwable $e) { diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 08f7a7376..6338d7625 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -39,7 +39,7 @@ class Controller extends BaseController } else { $team = $user->teams()->first(); } - if (is_null(data_get($user, 'email_verified_at'))){ + if (is_null(data_get($user, 'email_verified_at'))) { $user->email_verified_at = now(); $user->save(); } @@ -137,16 +137,28 @@ class Controller extends BaseController public function acceptInvitation() { try { - $invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail(); + $resetPassword = request()->query('reset-password'); + $invitationUuid = request()->route('uuid'); + $invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail(); $user = User::whereEmail($invitation->email)->firstOrFail(); - if (auth()->user()->id !== $user->id) { - abort(401); - } $invitationValid = $invitation->isValid(); if ($invitationValid) { + if ($resetPassword) { + $user->update([ + 'password' => Hash::make($invitationUuid), + 'force_password_reset' => true + ]); + } + if ($user->teams()->where('team_id', $invitation->team->id)->exists()) { + $invitation->delete(); + return redirect()->route('team.index'); + } $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]); - refreshSession($invitation->team); $invitation->delete(); + if (auth()->user()?->id !== $user->id) { + return redirect()->route('login'); + } + refreshSession($invitation->team); return redirect()->route('team.index'); } else { abort(401); diff --git a/app/Http/Middleware/CheckForcePasswordReset.php b/app/Http/Middleware/CheckForcePasswordReset.php index e8129deda..79b3819f7 100644 --- a/app/Http/Middleware/CheckForcePasswordReset.php +++ b/app/Http/Middleware/CheckForcePasswordReset.php @@ -24,7 +24,7 @@ class CheckForcePasswordReset } $force_password_reset = auth()->user()->force_password_reset; if ($force_password_reset) { - if ($request->routeIs('auth.force-password-reset') || $request->path() === 'livewire/message/force-password-reset') { + if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') { return $next($request); } return redirect()->route('auth.force-password-reset'); diff --git a/app/Http/Middleware/DecideWhatToDoWithUser.php b/app/Http/Middleware/DecideWhatToDoWithUser.php index 19a09125a..1381396df 100644 --- a/app/Http/Middleware/DecideWhatToDoWithUser.php +++ b/app/Http/Middleware/DecideWhatToDoWithUser.php @@ -11,6 +11,9 @@ class DecideWhatToDoWithUser { public function handle(Request $request, Closure $next): Response { + if(auth()?->user()?->currentTeam()){ + refreshSession(auth()->user()->currentTeam()); + } if (!auth()->user() || !isCloud() || isInstanceAdmin()) { if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { return redirect('boarding'); diff --git a/app/Http/Middleware/NOTUSEDIsBoardingFlow.php b/app/Http/Middleware/NOTUSEDIsBoardingFlow.php deleted file mode 100644 index 6e820f483..000000000 --- a/app/Http/Middleware/NOTUSEDIsBoardingFlow.php +++ /dev/null @@ -1,28 +0,0 @@ -showQueries()->color('orange'); - if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { - if (Str::startsWith($request->path(), 'invitations')) { - return $next($request); - } - return redirect('boarding'); - } - return $next($request); - } -} diff --git a/app/Http/Middleware/NOTUSEDIsSubscriptionValid.php b/app/Http/Middleware/NOTUSEDIsSubscriptionValid.php deleted file mode 100644 index 5774e5190..000000000 --- a/app/Http/Middleware/NOTUSEDIsSubscriptionValid.php +++ /dev/null @@ -1,45 +0,0 @@ -user() || !isCloud()) { - if ($request->path() === 'subscription') { - return redirect('/'); - } else { - return $next($request); - } - } - if (isSubscriptionActive() && $request->path() === 'subscription') { - // ray('active subscription Middleware'); - return redirect('/'); - } - if (isSubscriptionOnGracePeriod()) { - // ray('is_subscription_in_grace_period Middleware'); - return $next($request); - } - if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) { - // ray('SubscriptionValid Middleware'); - if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { - if (Str::startsWith($request->path(), 'invitations')) { - return $next($request); - } - return redirect('subscription'); - } else { - return $next($request); - } - } - return $next($request); - } -} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 7bb0df489..652fd2262 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -874,20 +874,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $environment_variables = $this->generate_environment_variables($ports); if (data_get($this->application, 'custom_labels')) { - if (base64_encode(base64_decode(data_get($this->application, 'custom_labels'), true)) === data_get($this->application, 'custom_labels')) { - ray('custom_labels is base64 encoded'); - } else { - ray('custom_labels is not base64 encoded'); - $this->application->custom_labels = str($this->application->custom_labels)->replace(',', "\n"); - $this->application->custom_labels = base64_encode(data_get($this->application, 'custom_labels')); - $this->application->save(); - } - - if (mb_detect_encoding(base64_decode($this->application->custom_labels), 'ASCII', true) === false) { - ray('custom_labels contains non-ascii characters'); - $this->application->custom_labels = base64_encode(str(implode(",", generateLabelsApplication($this->application, $this->preview)))->replace(',', "\n")); - $this->application->save(); - } + $this->application->parseContainerLabels(); $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels))); $labels = $labels->filter(function ($value, $key) { return !Str::startsWith($value, 'coolify.'); @@ -1354,10 +1341,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); } queue_next_deployment($this->application); if ($status === ApplicationDeploymentStatus::FINISHED->value) { - $this->application->environment->project->team->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); + $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); } if ($status === ApplicationDeploymentStatus::FAILED->value) { - $this->application->environment->project->team->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview)); + $this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview)); } } diff --git a/app/Jobs/CheckLogDrainContainerJob.php b/app/Jobs/CheckLogDrainContainerJob.php index e66a0c08a..eb677265b 100644 --- a/app/Jobs/CheckLogDrainContainerJob.php +++ b/app/Jobs/CheckLogDrainContainerJob.php @@ -63,19 +63,19 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted Sleep::for(10)->seconds(); if ($this->healthcheck()) { if ($this->server->log_drain_notification_sent) { - $this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server)); + $this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server)); $this->server->update(['log_drain_notification_sent' => false]); } return; } if (!$this->server->log_drain_notification_sent) { ray('Log drain container still unhealthy. Sending notification...'); - $this->server->team->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null)); + $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null)); $this->server->update(['log_drain_notification_sent' => true]); } } else { if ($this->server->log_drain_notification_sent) { - $this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server)); + $this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server)); $this->server->update(['log_drain_notification_sent' => false]); } } diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index 5599c6de4..9989ed8cb 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -17,6 +17,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Arr; +use Illuminate\Support\Sleep; class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted { @@ -37,10 +38,8 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted $this->handle(); } - public function handle() { - // ray("checking container statuses for {$this->server->id}"); try { if (!$this->server->isServerReady()) { return; diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 1ba855f4e..8a854128a 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -286,7 +286,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted if ($this->backup->save_s3) { $this->upload_to_s3(); } - $this->team->notify(new BackupSuccess($this->backup, $this->database)); + $this->team?->notify(new BackupSuccess($this->backup, $this->database)); $this->backup_log->update([ 'status' => 'success', 'message' => $this->backup_output, @@ -302,7 +302,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted ]); } send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage()); - $this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output)); + $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output)); throw $e; } } diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index f8df38a2a..cf7c6462a 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -32,9 +32,10 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted try { $server = $this->resource->destination->server; if (!$server->isFunctional()) { - $this->resource->delete(); + $this->resource->forceDelete(); return 'Server is not functional'; } + $this->resource->delete(); switch ($this->resource->type()) { case 'application': StopApplication::run($this->resource); @@ -56,10 +57,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted break; } if ($this->resource->type() === 'service') { - $this->resource->delete(); DeleteService::dispatch($this->resource); } else { - $this->resource->delete(); + $this->resource->forceDelete(); } } catch (\Throwable $e) { send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage()); diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php index cfe61598f..9dacea95c 100644 --- a/app/Jobs/ServerStatusJob.php +++ b/app/Jobs/ServerStatusJob.php @@ -54,7 +54,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted } else { $this->server->high_disk_usage_notification_sent = true; $this->server->save(); - $this->server->team->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage)); + $this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage)); } } else { DockerCleanupJob::dispatchSync($this->server); diff --git a/app/Livewire/Notifications/DiscordSettings.php b/app/Livewire/Notifications/DiscordSettings.php index cf8b116d4..f5f0d5591 100644 --- a/app/Livewire/Notifications/DiscordSettings.php +++ b/app/Livewire/Notifications/DiscordSettings.php @@ -52,7 +52,7 @@ class DiscordSettings extends Component public function sendTestNotification() { - $this->team->notify(new Test()); + $this->team?->notify(new Test()); $this->dispatch('success', 'Test notification sent.'); } } diff --git a/app/Livewire/Notifications/EmailSettings.php b/app/Livewire/Notifications/EmailSettings.php index f3b121851..cdbaef051 100644 --- a/app/Livewire/Notifications/EmailSettings.php +++ b/app/Livewire/Notifications/EmailSettings.php @@ -70,7 +70,7 @@ class EmailSettings extends Component } public function sendTestNotification() { - $this->team->notify(new Test($this->emails)); + $this->team?->notify(new Test($this->emails)); $this->dispatch('success', 'Test Email sent successfully.'); } public function instantSaveInstance() diff --git a/app/Livewire/Notifications/TelegramSettings.php b/app/Livewire/Notifications/TelegramSettings.php index 08f1be005..0f581a515 100644 --- a/app/Livewire/Notifications/TelegramSettings.php +++ b/app/Livewire/Notifications/TelegramSettings.php @@ -58,7 +58,7 @@ class TelegramSettings extends Component public function sendTestNotification() { - $this->team->notify(new Test()); + $this->team?->notify(new Test()); $this->dispatch('success', 'Test notification sent.'); } } diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 279e55651..d27fcff54 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -109,24 +109,7 @@ class General extends Component $this->application->isConfigurationChanged(true); } $this->isConfigurationChanged = $this->application->isConfigurationChanged(); - - if (data_get($this->application, 'custom_labels')) { - if (base64_encode(base64_decode(data_get($this->application, 'custom_labels'), true)) === data_get($this->application, 'custom_labels')) { - ray('custom_labels is base64 encoded'); - } else { - ray('custom_labels is not base64 encoded'); - $this->application->custom_labels = str($this->application->custom_labels)->replace(',', "\n"); - $this->application->custom_labels = base64_encode(data_get($this->application, 'custom_labels')); - $this->application->save(); - } - $this->customLabels = base64_decode(data_get($this->application, 'custom_labels')); - // // Fix for non-ascii characters - if (mb_detect_encoding($this->customLabels, 'ASCII', true) === false) { - ray('custom_labels contains non-ascii characters'); - $this->resetDefaultLabels(false); - } - } - + $this->customLabels = $this->application->parseContainerLabels(); $this->initialDockerComposeLocation = $this->application->docker_compose_location; $this->checkLabelUpdates(); } diff --git a/app/Livewire/Project/CloneProject.php b/app/Livewire/Project/CloneProject.php index 3a6a1db6f..7ce89f8de 100644 --- a/app/Livewire/Project/CloneProject.php +++ b/app/Livewire/Project/CloneProject.php @@ -19,12 +19,14 @@ class CloneProject extends Component public $servers; public ?Environment $environment = null; public ?int $selectedServer = null; + public ?int $selectedDestination = null; public ?Server $server = null; public $resources = []; public string $newProjectName = ''; protected $messages = [ 'selectedServer' => 'Please select a server.', + 'selectedDestination' => 'Please select a server & destination.', 'newProjectName' => 'Please enter a name for the new project.', ]; public function mount($project_uuid) @@ -34,7 +36,7 @@ class CloneProject extends Component $this->environment = $this->project->environments->where('name', $this->environment_name)->first(); $this->project_id = $this->project->id; $this->servers = currentTeam()->servers; - $this->newProjectName = $this->project->name . ' (clone)'; + $this->newProjectName = str($this->project->name . '-clone-' . (string)new Cuid2(7))->slug(); } public function render() @@ -42,9 +44,10 @@ class CloneProject extends Component return view('livewire.project.clone-project'); } - public function selectServer($server_id) + public function selectServer($server_id, $destination_id) { $this->selectedServer = $server_id; + $this->selectedDestination = $destination_id; $this->server = $this->servers->where('id', $server_id)->first(); } @@ -52,7 +55,7 @@ class CloneProject extends Component { try { $this->validate([ - 'selectedServer' => 'required', + 'selectedDestination' => 'required', 'newProjectName' => 'required', ]); $foundProject = Project::where('name', $this->newProjectName)->first(); @@ -81,7 +84,8 @@ class CloneProject extends Component 'fqdn' => generateFqdn($this->server, $uuid), 'status' => 'exited', 'environment_id' => $newEnvironment->id, - 'destination_id' => $this->selectedServer, + // This is not correct, but we need to set it to something + 'destination_id' => $this->selectedDestination, ]); $newApplication->save(); $environmentVaribles = $application->environment_variables()->get(); @@ -107,7 +111,7 @@ class CloneProject extends Component 'status' => 'exited', 'started_at' => null, 'environment_id' => $newEnvironment->id, - 'destination_id' => $this->selectedServer, + 'destination_id' => $this->selectedDestination, ]); $newDatabase->save(); $environmentVaribles = $database->environment_variables()->get(); @@ -133,7 +137,7 @@ class CloneProject extends Component $newService = $service->replicate()->fill([ 'uuid' => $uuid, 'environment_id' => $newEnvironment->id, - 'destination_id' => $this->selectedServer, + 'destination_id' => $this->selectedDestination, ]); $newService->save(); foreach ($newService->applications() as $application) { diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php index 3e2c3485c..d401a1022 100644 --- a/app/Livewire/Project/Service/Navbar.php +++ b/app/Livewire/Project/Service/Navbar.php @@ -2,6 +2,7 @@ namespace App\Livewire\Project\Service; +use App\Actions\Shared\PullImage; use App\Actions\Service\StartService; use App\Actions\Service\StopService; use App\Events\ServiceStatusChanged; @@ -69,4 +70,18 @@ class Navbar extends Component } ServiceStatusChanged::dispatch(); } + public function restart() + { + $this->checkDeployments(); + if ($this->isDeploymentProgress) { + $this->dispatch('error', 'There is a deployment in progress.'); + return; + } + PullImage::run($this->service); + $this->dispatch('image-pulled'); + StopService::run($this->service); + $this->service->parse(); + $activity = StartService::run($this->service); + $this->dispatch('newMonitorActivity', $activity->id); + } } diff --git a/app/Livewire/Settings/Email.php b/app/Livewire/Settings/Email.php index d005462a8..471c18e9c 100644 --- a/app/Livewire/Settings/Email.php +++ b/app/Livewire/Settings/Email.php @@ -106,7 +106,7 @@ class Email extends Component public function sendTestNotification() { - $this->settings->notify(new Test($this->emails)); + $this->settings?->notify(new Test($this->emails)); $this->dispatch('success', 'Test email sent.'); } } diff --git a/app/Models/Application.php b/app/Models/Application.php index 10f27e207..d3ddc4e43 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -2,8 +2,10 @@ namespace App\Models; +use App\Enums\ApplicationDeploymentStatus; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; use Spatie\Activitylog\Models\Activity; use Illuminate\Support\Str; @@ -13,6 +15,7 @@ use Visus\Cuid2\Cuid2; class Application extends BaseModel { + use SoftDeletes; protected $guarded = []; protected static function booted() @@ -338,7 +341,7 @@ class Application extends BaseModel } public function isDeploymentInprogress() { - $deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'in_progress')->count(); + $deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', ApplicationDeploymentStatus::QUEUED)->count(); if ($deployments > 0) { return true; } @@ -1024,4 +1027,24 @@ class Application extends BaseModel ]; } } + function parseContainerLabels(?ApplicationPreview $preview = null) + { + $customLabels = data_get($this, 'custom_labels'); + if (!$customLabels) { + return; + } + if (base64_encode(base64_decode($customLabels, true)) !== $customLabels) { + ray('custom_labels is not base64 encoded'); + $this->custom_labels = str($customLabels)->replace(',', "\n"); + $this->custom_labels = base64_encode($customLabels); + } + $customLabels = base64_decode($this->custom_labels); + if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { + ray('custom_labels contains non-ascii characters'); + $customLabels = str(implode(",", generateLabelsApplication($this, $preview)))->replace(',', "\n"); + } + $this->custom_labels = base64_encode($customLabels); + $this->save(); + return $customLabels; + } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 37a5ffe2a..790b30025 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -164,7 +164,7 @@ class Server extends BaseModel if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) { if ($this->unreachable_notification_sent === false) { ray('Server unreachable, sending notification...'); - $this->team->notify(new Unreachable($this)); + $this->team?->notify(new Unreachable($this)); $this->update(['unreachable_notification_sent' => true]); } $this->settings()->update([ @@ -401,7 +401,7 @@ class Server extends BaseModel } if (data_get($server, 'unreachable_notification_sent') === true) { - $server->team->notify(new Revived($server)); + $server->team?->notify(new Revived($server)); $server->update(['unreachable_notification_sent' => false]); } diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index b8e3f1a41..510395266 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -4,11 +4,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Cache; class ServiceApplication extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = []; protected static function booted() diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index 79c1dd176..0bde42c20 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -3,10 +3,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\SoftDeletes; class ServiceDatabase extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = []; protected static function booted() diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 8893632d3..33c00260e 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -6,10 +6,11 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneMariadb extends BaseModel { - use HasFactory; + use HasFactory,SoftDeletes; protected $guarded = []; protected $casts = [ diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index c041c9407..d08c04adb 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -5,10 +5,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneMongodb extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = []; protected static function booted() diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index a4691b9b2..02f8f82c4 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -5,10 +5,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneMysql extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = []; protected $casts = [ diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index e0db00d76..df9d28460 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -5,10 +5,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class StandalonePostgresql extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = []; protected $casts = [ diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index 95add8315..7ccde7f68 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -5,10 +5,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneRedis extends BaseModel { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = []; protected static function booted() diff --git a/app/Models/User.php b/app/Models/User.php index ee963a523..c099eb2b6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -98,7 +98,7 @@ class User extends Authenticatable implements SendsEmail } public function sendPasswordResetNotification($token): void { - $this->notify(new TransactionalEmailsResetPassword($token)); + $this?->notify(new TransactionalEmailsResetPassword($token)); } public function isAdmin() diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index c994199f2..47ea21e46 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -128,11 +128,6 @@ function allowedPathsForUnsubscribedAccounts() 'logout', 'waitlist', 'force-password-reset', - // 'livewire/message/force-password-reset', - // 'livewire/message/check-license', - // 'livewire/message/switch-team', - // 'livewire/message/subscription.pricing-plans', - // 'livewire/message/help', 'livewire/update' ]; } @@ -141,8 +136,6 @@ function allowedPathsForBoardingAccounts() return [ ...allowedPathsForUnsubscribedAccounts(), 'boarding', - // 'livewire/message/boarding.index', - // 'livewire/message/activity-monitor', 'livewire/update' ]; } @@ -151,9 +144,6 @@ function allowedPathsForInvalidAccounts() { 'logout', 'verify', 'force-password-reset', - // 'livewire/message/force-password-reset', - // 'livewire/message/verify-email', - // 'livewire/message/help', 'livewire/update' ]; } diff --git a/config/sentry.php b/config/sentry.php index 076edb85e..b5b534784 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -3,11 +3,11 @@ return [ // @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - 'dsn' => 'https://bea22abf110618b07252032aa2e07859@o1082494.ingest.sentry.io/4505347448045568', + 'dsn' => 'https://1bbc8f762199a52aee39196adb3e8d1a@o1082494.ingest.sentry.io/4505347448045568', // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.161', + 'release' => '4.0.0-beta.162', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 8f7d7b0d4..bf223006f 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ - */ -class StandaloneMongodbFactory extends Factory -{ - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return [ - // - ]; - } -} diff --git a/database/migrations/2023_12_13_110214_add_soft_deletes.php b/database/migrations/2023_12_13_110214_add_soft_deletes.php new file mode 100644 index 000000000..ab7b562b4 --- /dev/null +++ b/database/migrations/2023_12_13_110214_add_soft_deletes.php @@ -0,0 +1,71 @@ +softDeletes(); + }); + Schema::table('standalone_postgresqls', function (Blueprint $table) { + $table->softDeletes(); + }); + Schema::table('standalone_redis', function (Blueprint $table) { + $table->softDeletes(); + }); + Schema::table('standalone_mongodbs', function (Blueprint $table) { + $table->softDeletes(); + }); + Schema::table('standalone_mysqls', function (Blueprint $table) { + $table->softDeletes(); + }); + Schema::table('standalone_mariadbs', function (Blueprint $table) { + $table->softDeletes(); + }); + Schema::table('service_applications', function (Blueprint $table) { + $table->softDeletes(); + }); + Schema::table('service_databases', function (Blueprint $table) { + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('standalone_postgresqls', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('standalone_redis', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('standalone_mongodbs', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('standalone_mysqls', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('standalone_mariadbs', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('service_applications', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + Schema::table('service_databases', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + + } +}; diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 87b4bb6b5..a9f09efa3 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -56,7 +56,7 @@ services: ports: - "${FORWARD_SOKETI_PORT:-6001}:6001" environment: - SOKETI_DEBUG: "true" + SOKETI_DEBUG: "false" SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}" SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}" SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" @@ -70,6 +70,8 @@ services: volumes: - .:/var/www/html:cached command: sh -c "npm install && npm run dev" + networks: + - coolify testing-host: <<: *testing-host-base container_name: coolify-testing-host @@ -77,15 +79,8 @@ services: - /:/host - /var/run/docker.sock:/var/run/docker.sock - /data/coolify/:/data/coolify - # - coolify-data-dev:/data/coolify - # remote-host: - # <<: *testing-host-base - # container_name: coolify-remote-host - # volumes: - # - /:/host - # - /var/run/docker.sock:/var/run/docker.sock - # - /data/coolify/:/data/coolify - # # - coolify-data-dev:/data/coolify + networks: + - coolify mailpit: image: "axllent/mailpit:latest" container_name: coolify-mail @@ -115,3 +110,9 @@ volumes: coolify-pg-data-dev: coolify-redis-data-dev: coolify-minio-data-dev: + + +networks: + coolify: + name: coolify + external: false diff --git a/docker/dev-ssu/Dockerfile b/docker/dev-ssu/Dockerfile index 58941b73f..df98985f4 100644 --- a/docker/dev-ssu/Dockerfile +++ b/docker/dev-ssu/Dockerfile @@ -2,7 +2,7 @@ FROM serversideup/php:8.2-fpm-nginx ARG TARGETPLATFORM # https://github.com/cloudflare/cloudflared/releases -ARG CLOUDFLARED_VERSION=2023.8.2 +ARG CLOUDFLARED_VERSION=2023.10.0 ARG POSTGRES_VERSION=15 RUN apt-get update diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/dependencies.d/init-setup b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/dependencies.d/init-setup new file mode 100644 index 000000000..e69de29bb diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type new file mode 100644 index 000000000..3d92b15f2 --- /dev/null +++ b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type @@ -0,0 +1 @@ +oneshot \ No newline at end of file diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up new file mode 100644 index 000000000..e974e54cc --- /dev/null +++ b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up @@ -0,0 +1,5 @@ +#!/command/execlineb -P +foreground { composer -d /var/www/html/ install } +foreground { php /var/www/html/artisan migrate --step } +foreground { php /var/www/html/artisan dev:init } + diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/dependencies.d/init-setup b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/dependencies.d/init-setup new file mode 100644 index 000000000..e69de29bb diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup new file mode 100644 index 000000000..e69de29bb diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker b/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker new file mode 100644 index 000000000..e69de29bb diff --git a/docker/prod-ssu/Dockerfile b/docker/prod-ssu/Dockerfile index dac8c56a9..79c0a839c 100644 --- a/docker/prod-ssu/Dockerfile +++ b/docker/prod-ssu/Dockerfile @@ -15,7 +15,7 @@ FROM serversideup/php:8.2-fpm-nginx ARG TARGETPLATFORM # https://github.com/cloudflare/cloudflared/releases -ARG CLOUDFLARED_VERSION=2023.8.2 +ARG CLOUDFLARED_VERSION=2023.10.0 ARG POSTGRES_VERSION=15 WORKDIR /var/www/html diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/up b/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/up index 3b252b782..b563c067f 100644 --- a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/up +++ b/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/up @@ -1,3 +1,3 @@ #!/command/execlineb -P s6-setuidgid webuser -php /var/www/html/artisan app:init +php /var/www/html/artisan app:init --cleanup diff --git a/resources/css/app.css b/resources/css/app.css index 3b9d9ca93..868cbd9c0 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -5,25 +5,32 @@ html { @apply text-neutral-400; } + body { @apply text-sm antialiased scrollbar; } + button[isError] { @apply bg-red-600 hover:bg-red-700; } + .scrollbar { @apply scrollbar-thumb-coollabs-100 scrollbar-track-coolgray-200 scrollbar-w-2; } + .main { @apply pt-4 pl-24 pr-10 mx-auto; } + .custom-modal { @apply flex flex-col gap-2 px-8 py-4 border bg-base-100 border-coolgray-200; } + .label-text, label { @apply text-neutral-400; } + .navbar-main { @apply flex items-end gap-6 py-2 border-b-2 border-solid border-coolgray-200; } @@ -31,89 +38,117 @@ label { .loading { @apply w-4 text-warning; } + h1 { @apply text-3xl font-bold text-white; } + h2 { @apply text-2xl font-bold text-white; } + h3 { @apply text-xl font-bold text-white; } + h4 { @apply text-base font-bold text-white; } + a { @apply text-neutral-400 hover:text-white link link-hover hover:bg-transparent; } + .kbd-custom { @apply px-2 text-xs border border-dashed rounded border-neutral-700 text-warning; } + .icon { @apply w-6 h-6; } + .icon:hover { @apply text-white; } + .box { @apply flex p-2 transition-colors cursor-pointer min-h-[4rem] bg-coolgray-100 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem]; } + .box-without-bg { - @apply flex p-2 transition-colors h-16 min-h-full hover:text-white hover:no-underline min-h-[4rem]; + @apply flex p-2 transition-colors min-h-full hover:text-white hover:no-underline min-h-[4rem]; } + .description { @apply pt-2 text-xs font-bold text-neutral-500 group-hover:text-white; } + .lds-heart { animation: lds-heart 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1); } + @keyframes lds-heart { 0% { transform: scale(1); } + 5% { transform: scale(1.2); } + 39% { transform: scale(0.85); } + 45% { transform: scale(1); } + 60% { transform: scale(0.95); } + 100% { transform: scale(0.9); } } + .bg-coollabs-gradient { @apply text-transparent text-white bg-gradient-to-r from-purple-500 via-pink-500 to-red-500; } + .text-helper { @apply inline-block font-bold text-warning; } + table { @apply min-w-full divide-y divide-coolgray-200; } + thead { @apply uppercase; } + tbody { @apply divide-y divide-coolgray-200; } + tr { @apply text-neutral-400; } + tr th { @apply px-3 py-3.5 text-left text-white; } + tr th:first-child { @apply py-3.5 pl-4 pr-3 sm:pl-6; } + tr td { @apply px-3 py-4 whitespace-nowrap; } + tr td:first-child { @apply pl-4 pr-3 font-bold sm:pl-6; } @@ -121,12 +156,15 @@ tr td:first-child { .buyme { @apply block px-3 py-2 mt-10 text-sm font-semibold leading-6 text-center text-white rounded-md shadow-sm bg-coolgray-200 hover:bg-coolgray-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coolgray-200 hover:no-underline; } + .subtitle { @apply pt-2 pb-10; } + .fullscreen { - @apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4 ; + @apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4; } + input.input-sm { @apply pr-10; } diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index b2111d561..ed288f9ee 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -24,20 +24,20 @@
@csrf @env('local') - + - - - {{ __('auth.forgot_password') }}? - - @else - - - - {{ __('auth.forgot_password') }}? - + + + {{ __('auth.forgot_password') }}? + + @else + + + + {{ __('auth.forgot_password') }}? + @endenv {{ __('auth.login') }} @if (!$is_registration_enabled) diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php index ae417a0d9..6b248cfee 100644 --- a/resources/views/auth/reset-password.blade.php +++ b/resources/views/auth/reset-password.blade.php @@ -1,13 +1,13 @@
-
+
-

{{ __('auth.reset_password') }}

+ {{ __('auth.reset_password') }}
diff --git a/resources/views/components/forms/input.blade.php b/resources/views/components/forms/input.blade.php index 4e9fffef5..66f0791d1 100644 --- a/resources/views/components/forms/input.blade.php +++ b/resources/views/components/forms/input.blade.php @@ -22,18 +22,20 @@
@endif - merge(['class' => $defaultClass . ' pl-10']) }} @required($required) - wire:model={{ $id }} wire:dirty.class.remove='text-white' wire:dirty.class="input-warning" - wire:loading.attr="disabled" type="{{ $type }}" @readonly($readonly) @disabled($disabled) - id="{{ $id }}" name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}" + merge(['class' => $defaultClass . ' pl-10']) }} + @required($required) @if ($id !== 'null') wire:model={{ $id }} @endif + wire:dirty.class.remove='text-white' wire:dirty.class="input-warning" wire:loading.attr="disabled" + type="{{ $type }}" @readonly($readonly) @disabled($disabled) id="{{ $id }}" + name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}" aria-placeholder="{{ $attributes->get('placeholder') }}">
@else - merge(['class' => $defaultClass]) }} @required($required) @readonly($readonly) - wire:model={{ $id }} wire:dirty.class.remove='text-white' wire:dirty.class="input-warning" + merge(['class' => $defaultClass]) }} @required($required) @readonly($readonly) + @if ($id !== 'null') wire:model={{ $id }} @endif wire:dirty.class.remove='text-white' wire:dirty.class="input-warning" wire:loading.attr="disabled" type="{{ $type }}" @disabled($disabled) - id="{{ $id }}" name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"> + @if ($id !== 'null') id={{ $id }} @endif name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"> @endif @if (!$label && $helper) diff --git a/resources/views/components/services/navbar.blade.php b/resources/views/components/services/navbar.blade.php index 84ad7c6e1..c25bb3eac 100644 --- a/resources/views/components/services/navbar.blade.php +++ b/resources/views/components/services/navbar.blade.php @@ -1,4 +1,4 @@ -