a lot hehe

This commit is contained in:
Andras Bacsai
2023-06-01 12:15:33 +02:00
parent c8f70a4e3b
commit 0aa816b4f2
42 changed files with 570 additions and 249 deletions

View File

@@ -8,7 +8,9 @@ GROUPID=
PROJECT_PATH_ON_HOST=/Users/your-username-here/code/coollabsio/coolify PROJECT_PATH_ON_HOST=/Users/your-username-here/code/coollabsio/coolify
SERVEO_URL=<for receiving webhooks locally https://serveo.net/> SERVEO_URL=<for receiving webhooks locally https://serveo.net/>
MUX_ENABLED=false MUX_ENABLED=false
# If you are using the included Buggregator
RAY_HOST=ray@host.docker.internal RAY_HOST=ray@host.docker.internal
RAY_PORT=8001
############################################################################################################ ############################################################################################################
APP_NAME=Coolify APP_NAME=Coolify
@@ -19,6 +21,10 @@ APP_DEBUG=true
APP_URL=http://localhost APP_URL=http://localhost
APP_PORT=8000 APP_PORT=8000
MAIL_MAILER=smtp
MAIL_HOST=coolify-mail
MAIL_PORT=1025
SESSION_DRIVER=database SESSION_DRIVER=database
DUSK_DRIVER_URL=http://selenium:4444 DUSK_DRIVER_URL=http://selenium:4444

View File

@@ -1,4 +1,4 @@
# Secrets related to pushing to GH, Sync files to BunnyCDN etc. # Secrets related to pushing to GH, Sync files to BunnyCDN etc. Only for maintainers.
# Not related to Coolify, but to how we publish new versions. # Not related to Coolify, but to how we publish new versions.
GITHUB_TOKEN= GITHUB_TOKEN=

View File

@@ -4,7 +4,7 @@ namespace App\Http\Livewire\Notifications;
use App\Models\Server; use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use App\Notifications\DemoNotification; use App\Notifications\TestNotification;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Livewire\Component; use Livewire\Component;
@@ -13,31 +13,40 @@ class DiscordSettings extends Component
public Team|Server $model; public Team|Server $model;
protected $rules = [ protected $rules = [
'model.extra_attributes.discord_webhook' => 'nullable|url', 'model.smtp_attributes.discord_active' => 'nullable|boolean',
'model.extra_attributes.discord_active' => 'nullable|boolean', 'model.smtp_attributes.discord_webhook' => 'required|url',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'model.extra_attributes.discord_webhook' => 'Discord Webhook', 'model.smtp_attributes.discord_webhook' => 'Discord Webhook',
]; ];
public function mount($model) public function mount($model)
{ {
// //
} }
public function submit() public function instantSave()
{
try {
$this->submit();
} catch (\Exception $e) {
$this->model->smtp_attributes->discord_active = false;
$this->addError('model.smtp_attributes.discord_webhook', $e->getMessage());
}
}
private function saveModel()
{ {
$this->resetErrorBag();
$this->validate();
$this->model->save(); $this->model->save();
if (is_a($this->model, Team::class)) { if (is_a($this->model, Team::class)) {
session(['currentTeam' => $this->model]); session(['currentTeam' => $this->model]);
} }
} }
public function submit()
{
$this->resetErrorBag();
$this->validate();
$this->saveModel();
}
public function sendTestNotification() public function sendTestNotification()
{ {
Notification::send($this->model, new DemoNotification); Notification::send($this->model, new TestNotification);
}
public function render()
{
return view('livewire.notifications.discord-settings');
} }
} }

View File

@@ -4,7 +4,7 @@ namespace App\Http\Livewire\Notifications;
use App\Models\Server; use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use App\Notifications\DemoNotification; use App\Notifications\TestNotification;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Livewire\Component; use Livewire\Component;
@@ -13,27 +13,27 @@ class EmailSettings extends Component
public Team|Server $model; public Team|Server $model;
protected $rules = [ protected $rules = [
'model.extra_attributes.smtp_active' => 'nullable|boolean', 'model.smtp_attributes.smtp_active' => 'nullable|boolean',
'model.extra_attributes.from_address' => 'nullable', 'model.smtp_attributes.from_address' => 'required',
'model.extra_attributes.from_name' => 'nullable', 'model.smtp_attributes.from_name' => 'required',
'model.extra_attributes.recipients' => 'nullable', 'model.smtp_attributes.recipients' => 'required',
'model.extra_attributes.smtp_host' => 'nullable', 'model.smtp_attributes.smtp_host' => 'required',
'model.extra_attributes.smtp_port' => 'nullable', 'model.smtp_attributes.smtp_port' => 'required',
'model.extra_attributes.smtp_encryption' => 'nullable', 'model.smtp_attributes.smtp_encryption' => 'nullable',
'model.extra_attributes.smtp_username' => 'nullable', 'model.smtp_attributes.smtp_username' => 'nullable',
'model.extra_attributes.smtp_password' => 'nullable', 'model.smtp_attributes.smtp_password' => 'nullable',
'model.extra_attributes.smtp_timeout' => 'nullable', 'model.smtp_attributes.smtp_timeout' => 'nullable',
'model.smtp_attributes.test_address' => 'nullable',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'model.extra_attributes.from_address' => 'From Address', 'model.smtp_attributes.from_address' => 'From Address',
'model.extra_attributes.from_name' => 'From Name', 'model.smtp_attributes.from_name' => 'From Name',
'model.extra_attributes.recipients' => 'Recipients', 'model.smtp_attributes.recipients' => 'Recipients',
'model.extra_attributes.smtp_host' => 'Host', 'model.smtp_attributes.smtp_host' => 'Host',
'model.extra_attributes.smtp_port' => 'Port', 'model.smtp_attributes.smtp_port' => 'Port',
'model.extra_attributes.smtp_encryption' => 'Encryption', 'model.smtp_attributes.smtp_encryption' => 'Encryption',
'model.extra_attributes.smtp_username' => 'Username', 'model.smtp_attributes.smtp_username' => 'Username',
'model.extra_attributes.smtp_password' => 'Password', 'model.smtp_attributes.smtp_password' => 'Password',
'model.extra_attributes.smtp_timeout' => 'Timeout',
]; ];
public function mount($model) public function mount($model)
{ {
@@ -43,17 +43,17 @@ class EmailSettings extends Component
{ {
$this->resetErrorBag(); $this->resetErrorBag();
$this->validate(); $this->validate();
$this->saveModel();
}
private function saveModel()
{
$this->model->save(); $this->model->save();
if (is_a($this->model, Team::class)) { if (is_a($this->model, Team::class)) {
session(['currentTeam' => $this->model]); session(['currentTeam' => $this->model]);
} }
} }
public function sendTestNotification() public function instantSave()
{ {
Notification::send($this->model, new DemoNotification); $this->saveModel();
}
public function render()
{
return view('livewire.notifications.email-settings');
} }
} }

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Livewire\Notifications;
use App\Models\Server;
use App\Models\Team;
use App\Notifications\TestNotification;
use Livewire\Component;
use Notification;
class Test extends Component
{
public Team|Server $model;
public function sendTestNotification()
{
Notification::send($this->model, new TestNotification);
$this->emit('saved', 'Test notification sent.');
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Livewire\Settings;
use App\Models\InstanceSettings;
use Livewire\Component;
class Email extends Component
{
public InstanceSettings $model;
protected $rules = [
'model.extra_attributes.from_address' => 'nullable',
'model.extra_attributes.from_name' => 'nullable',
'model.extra_attributes.recipients' => 'nullable',
'model.extra_attributes.smtp_host' => 'nullable',
'model.extra_attributes.smtp_port' => 'nullable',
'model.extra_attributes.smtp_encryption' => 'nullable',
'model.extra_attributes.smtp_username' => 'nullable',
'model.extra_attributes.smtp_password' => 'nullable',
'model.extra_attributes.smtp_timeout' => 'nullable',
];
protected $validationAttributes = [
'model.extra_attributes.from_address' => 'From Address',
'model.extra_attributes.from_name' => 'From Name',
'model.extra_attributes.recipients' => 'Recipients',
'model.extra_attributes.smtp_host' => 'Host',
'model.extra_attributes.smtp_port' => 'Port',
'model.extra_attributes.smtp_encryption' => 'Encryption',
'model.extra_attributes.smtp_username' => 'Username',
'model.extra_attributes.smtp_password' => 'Password',
];
public function mount($model)
{
//
}
public function render()
{
return view('livewire.settings.email');
}
}

View File

@@ -22,16 +22,23 @@ class Server extends BaseModel
'port', 'port',
'team_id', 'team_id',
'private_key_id', 'private_key_id',
'extra_attributes',
'smtp_attributes',
]; ];
public $casts = [ public $casts = [
'extra_attributes' => SchemalessAttributes::class, 'extra_attributes' => SchemalessAttributes::class,
'smtp_attributes' => SchemalessAttributes::class,
]; ];
public function scopeWithExtraAttributes(): Builder public function scopeWithExtraAttributes(): Builder
{ {
return $this->extra_attributes->modelScope(); return $this->extra_attributes->modelScope();
} }
public function scopeWithSmtpAttributes(): Builder
{
return $this->smtp_attributes->modelScope();
}
public function standaloneDockers() public function standaloneDockers()
{ {
@@ -43,8 +50,6 @@ class Server extends BaseModel
return $this->hasMany(SwarmDocker::class); return $this->hasMany(SwarmDocker::class);
} }
public function privateKey() public function privateKey()
{ {
return $this->belongsTo(PrivateKey::class); return $this->belongsTo(PrivateKey::class);

View File

@@ -2,42 +2,41 @@
namespace App\Models; namespace App\Models;
use App\Notifications\Channels\SendsCoolifyEmail; use App\Notifications\Channels\SendsEmail;
use App\Notifications\Channels\SendsDiscord; use App\Notifications\Channels\SendsDiscord;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes; use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
class Team extends BaseModel implements SendsDiscord, SendsCoolifyEmail class Team extends BaseModel implements SendsDiscord, SendsEmail
{ {
use Notifiable; use Notifiable;
protected $casts = [ protected $casts = [
'extra_attributes' => SchemalessAttributes::class, 'smtp_attributes' => SchemalessAttributes::class,
'personal_team' => 'boolean', 'personal_team' => 'boolean',
]; ];
protected $fillable = [ protected $fillable = [
'id', 'id',
'name', 'name',
'personal_team', 'personal_team',
'extra_attributes', 'smtp_attributes',
]; ];
public function routeNotificationForDiscord() public function routeNotificationForDiscord()
{ {
return $this->extra_attributes->get('discord_webhook'); return $this->smtp_attributes->get('discord_webhook');
} }
public function routeNotificationForCoolifyEmail() public function routeNotificationForEmail(string $attribute = 'recipients')
{ {
$recipients = $this->extra_attributes->get('recipients', ''); $recipients = $this->smtp_attributes->get($attribute, '');
return explode(PHP_EOL, $recipients); return explode(PHP_EOL, $recipients);
} }
public function scopeWithExtraAttributes(): Builder public function scopeWithExtraAttributes(): Builder
{ {
return $this->extra_attributes->modelScope(); return $this->smtp_attributes->modelScope();
} }
public function projects() public function projects()

View File

@@ -8,10 +8,11 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens; use Laravel\Sanctum\HasApiTokens;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable class User extends Authenticatable
{ {
use HasApiTokens, HasFactory, Notifiable; use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;
protected $fillable = [ protected $fillable = [
'id', 'id',
'name', 'name',

View File

@@ -1,44 +0,0 @@
<?php
namespace App\Notifications\Channels;
use Illuminate\Mail\Message;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CoolifyEmailChannel
{
/**
* Send the given notification.
*/
public function send(SendsCoolifyEmail $notifiable, Notification $notification): void
{
$this->bootConfigs($notifiable);
$bcc = $notifiable->routeNotificationForCoolifyEmail();
$mailMessage = $notification->toMail($notifiable);
Mail::send([], [], fn(Message $message) => $message
->from(
$notifiable->extra_attributes?->get('from_address'),
$notifiable->extra_attributes?->get('from_name')
)
->bcc($bcc)
->subject($mailMessage->subject)
->html((string)$mailMessage->render())
);
}
private function bootConfigs($notifiable): void
{
config()->set('mail.mailers.smtp', [
"transport" => "smtp",
"host" => $notifiable->extra_attributes?->get('smtp_host'),
"port" => $notifiable->extra_attributes?->get('smtp_port'),
"encryption" => $notifiable->extra_attributes?->get('smtp_encryption'),
"username" => $notifiable->extra_attributes?->get('smtp_username'),
"password" => $notifiable->extra_attributes?->get('smtp_password'),
"timeout" => $notifiable->extra_attributes?->get('smtp_timeout'),
"local_domain" => null,
]);
}
}

View File

@@ -13,9 +13,7 @@ class DiscordChannel
public function send(SendsDiscord $notifiable, Notification $notification): void public function send(SendsDiscord $notifiable, Notification $notification): void
{ {
$message = $notification->toDiscord($notifiable); $message = $notification->toDiscord($notifiable);
$webhookUrl = $notifiable->routeNotificationForDiscord(); $webhookUrl = $notifiable->routeNotificationForDiscord();
dispatch(new SendMessageToDiscordJob($message, $webhookUrl)); dispatch(new SendMessageToDiscordJob($message, $webhookUrl));
} }
} }

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Notifications\Channels;
use Illuminate\Mail\Message;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class EmailChannel
{
/**
* Send the given notification.
*/
public function send(SendsEmail $notifiable, Notification $notification): void
{
$this->bootConfigs($notifiable);
if ($notification instanceof \App\Notifications\TestNotification) {
$bcc = $notifiable->routeNotificationForEmail('test_address');
if (count($bcc) === 1) {
$bcc = $notifiable->routeNotificationForEmail();
}
} else {
$bcc = $notifiable->routeNotificationForEmail();
}
$mailMessage = $notification->toMail($notifiable);
Mail::send(
[],
[],
fn (Message $message) => $message
->from(
$notifiable->smtp_attributes?->get('from_address'),
$notifiable->smtp_attributes?->get('from_name')
)
->cc($bcc)
->bcc($bcc)
->subject($mailMessage->subject)
->html((string)$mailMessage->render())
);
}
private function bootConfigs($notifiable): void
{
config()->set('mail.default', 'smtp');
config()->set('mail.mailers.smtp', [
"transport" => "smtp",
"host" => $notifiable->smtp_attributes?->get('smtp_host'),
"port" => $notifiable->smtp_attributes?->get('smtp_port'),
"encryption" => $notifiable->smtp_attributes?->get('smtp_encryption'),
"username" => $notifiable->smtp_attributes?->get('smtp_username'),
"password" => $notifiable->smtp_attributes?->get('smtp_password'),
"timeout" => $notifiable->smtp_attributes?->get('smtp_timeout'),
"local_domain" => null,
]);
}
}

View File

@@ -1,8 +0,0 @@
<?php
namespace App\Notifications\Channels;
interface SendsCoolifyEmail
{
public function routeNotificationForCoolifyEmail();
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Notifications\Channels;
interface SendsEmail
{
public function routeNotificationForEmail();
}

View File

@@ -2,14 +2,14 @@
namespace App\Notifications; namespace App\Notifications;
use App\Notifications\Channels\CoolifyEmailChannel; use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\DiscordChannel;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
class DemoNotification extends Notification implements ShouldQueue class TestNotification extends Notification implements ShouldQueue
{ {
use Queueable; use Queueable;
@@ -29,8 +29,8 @@ class DemoNotification extends Notification implements ShouldQueue
public function via(object $notifiable): array public function via(object $notifiable): array
{ {
$channels = []; $channels = [];
$notifiable->extra_attributes?->get('smtp_active') && $channels[] = CoolifyEmailChannel::class; $notifiable->smtp_attributes?->get('smtp_active') && $channels[] = EmailChannel::class;
$notifiable->extra_attributes?->get('discord_active') && $channels[] = DiscordChannel::class; $notifiable->smtp_attributes?->get('discord_active') && $channels[] = DiscordChannel::class;
return $channels; return $channels;
} }
@@ -40,15 +40,14 @@ class DemoNotification extends Notification implements ShouldQueue
public function toMail(object $notifiable): MailMessage public function toMail(object $notifiable): MailMessage
{ {
return (new MailMessage) return (new MailMessage)
->subject('Coolify demo notification') ->subject('Coolify Test Notification')
->line('Welcome to Coolify!') ->line('Congratulations!')
->action('Go to dashboard', url('/')) ->line('You have successfully received a test Email notification from Coolify. 🥳');
->line('We need your attention for disk usage.');
} }
public function toDiscord(object $notifiable): string public function toDiscord(object $notifiable): string
{ {
return 'Welcome to Coolify! We need your attention for disk usage. [Go to dashboard]('.url('/').')'; return 'You have successfully received a test Discord notification from Coolify. 🥳 [Go to your dashboard](' . url('/') . ')';
} }
/** /**

View File

@@ -13,6 +13,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Contracts\RegisterResponse;
use Laravel\Fortify\Fortify; use Laravel\Fortify\Fortify;
class FortifyServiceProvider extends ServiceProvider class FortifyServiceProvider extends ServiceProvider
@@ -22,7 +23,17 @@ class FortifyServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
// $this->app->instance(RegisterResponse::class, new class implements RegisterResponse
{
public function toResponse($request)
{
// First user (root) will be redirected to /settings instead of / on registration.
if ($request->user()->currentTeam->id === 0) {
return redirect('/settings');
}
return redirect('/');
}
});
} }
/** /**
@@ -30,6 +41,7 @@ class FortifyServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::registerView(function () { Fortify::registerView(function () {
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
if (!$settings->is_registration_enabled) { if (!$settings->is_registration_enabled) {
@@ -58,10 +70,21 @@ class FortifyServiceProvider extends ServiceProvider
Fortify::requestPasswordResetLinkView(function () { Fortify::requestPasswordResetLinkView(function () {
return view('auth.forgot-password'); return view('auth.forgot-password');
}); });
Fortify::createUsersUsing(CreateNewUser::class); Fortify::resetPasswordView(function ($request) {
return view('auth.reset-password', ['request' => $request]);
});
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class); Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class); Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
Fortify::confirmPasswordView(function () {
return view('auth.confirm-password');
});
Fortify::twoFactorChallengeView(function () {
return view('auth.two-factor-challenge');
});
RateLimiter::for('login', function (Request $request) { RateLimiter::for('login', function (Request $request) {
$email = (string) $request->email; $email = (string) $request->email;

View File

@@ -13,7 +13,7 @@ return [
| |
*/ */
'default' => env('MAIL_MAILER', 'smtp'), 'default' => env('MAIL_MAILER', null),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@@ -16,7 +16,7 @@ return new class extends Migration
$table->string('uuid')->unique(); $table->string('uuid')->unique();
$table->string('name'); $table->string('name');
$table->boolean('personal_team')->default(false); $table->boolean('personal_team')->default(false);
$table->schemalessAttributes('extra_attributes'); $table->schemalessAttributes('smtp_attributes');
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@@ -22,6 +22,7 @@ return new class extends Migration
$table->foreignId('team_id'); $table->foreignId('team_id');
$table->foreignId('private_key_id'); $table->foreignId('private_key_id');
$table->schemalessAttributes('extra_attributes'); $table->schemalessAttributes('extra_attributes');
$table->schemalessAttributes('smtp_attributes');
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@@ -64,13 +64,17 @@ services:
- "./_data/coolify/proxy/testing-host-2:/data/coolify/proxy" - "./_data/coolify/proxy/testing-host-2:/data/coolify/proxy"
mailpit: mailpit:
image: 'axllent/mailpit:latest' image: 'axllent/mailpit:latest'
container_name: coolify-mail
ports: ports:
- '${FORWARD_MAILPIT_PORT:-1025}:1025' - '${FORWARD_MAILPIT_PORT:-1025}:1025'
- '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025' - '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025'
networks:
- coolify
buggregator: buggregator:
image: ghcr.io/buggregator/server:latest image: ghcr.io/buggregator/server:latest
container_name: coolify-debug container_name: coolify-debug
ports: ports:
- 23517:8000 - 8001:8000
networks: networks:
- coolify - coolify

View File

@@ -1,11 +1,14 @@
{ {
"auth.login": "Login", "auth.login": "Login",
"auth.already-registered": "Already registered?", "auth.already_registered": "Already registered?",
"auth.register-now": "Register now!", "auth.confirm_password": "Confirm password",
"auth.forgot_password": "Forgot password",
"auth.forgot_password_send_email": "Send password reset link via email",
"auth.register_now": "Register now!",
"auth.logout": "Logout", "auth.logout": "Logout",
"auth.register": "Register", "auth.register": "Register",
"auth.registration_disabled": "Registration is disabled. Please contact the administrator.", "auth.registration_disabled": "Registration is disabled. Please contact the administrator.",
"auth.reset_password": "Reset Password", "auth.reset_password": "Reset password",
"auth.failed": "These credentials do not match our records.", "auth.failed": "These credentials do not match our records.",
"auth.failed.password": "The provided password is incorrect.", "auth.failed.password": "The provided password is incorrect.",
"auth.failed.email": "We can't find a user with that e-mail address.", "auth.failed.email": "We can't find a user with that e-mail address.",
@@ -13,6 +16,8 @@
"input.name": "Name", "input.name": "Name",
"input.email": "Email", "input.email": "Email",
"input.password": "Password", "input.password": "Password",
"input.password.again": "Password Again", "input.password.again": "Password again",
"input.code": "One-time code",
"input.recovery_code": "Recovery code",
"button.save": "Save" "button.save": "Save"
} }

View File

@@ -55,11 +55,14 @@ h1 {
@apply text-3xl font-bold pb-4 text-white; @apply text-3xl font-bold pb-4 text-white;
} }
h2 { h2 {
@apply text-2xl font-bold pb-4 text-white; @apply text-2xl font-bold py-4 text-white;
} }
h3 { h3 {
@apply text-xl font-bold py-4 text-white; @apply text-xl font-bold py-4 text-white;
} }
h4 {
@apply text-base font-bold pb-4 text-white;
}
a { a {
@apply text-neutral-400 hover:text-white text-sm link link-hover hover:bg-transparent; @apply text-neutral-400 hover:text-white text-sm link link-hover hover:bg-transparent;
} }

View File

@@ -0,0 +1,28 @@
<x-layout-simple>
<div class="flex items-center justify-center h-screen">
<div>
<div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
</div>
<div class="w-96">
<form action="/user/confirm-password" method="POST" class="flex flex-col gap-2">
@csrf
<x-forms.input required type="password" name="password " label="{{ __('input.password') }}"
autofocus />
<x-forms.button type="submit">{{ __('auth.confirm_password') }}</x-forms.button>
</form>
@if ($errors->any())
<div class="text-center text-error">
<span>{{ __('auth.failed') }}</span>
</div>
@endif
@if (session('status'))
<div class="mb-4 text-sm font-medium text-green-600">
{{ session('status') }}
</div>
@endif
</div>
</div>
</div>
</x-layout-simple>

View File

@@ -1,50 +1,32 @@
<x-layout-simple> <x-layout-simple>
Forgot Password <div class="flex items-center justify-center h-screen">
<form>
@csrf
<x-forms.input required value="test@example.com" type="email" name="email" label="{{ __('input.email') }}"
autofocus />
</form>
@if (session('status'))
<div class="mb-4 text-sm font-medium text-green-600">
{{ session('status') }}
</div>
@endif
{{-- <div class="flex items-center justify-center h-screen">
<div> <div>
<div class="pb-8 text-5xl font-bold tracking-tight text-center text-white">Coolify</div> <div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
</div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h1 class="pb-0">{{ __('auth.login') }}</h1> <h1 class="pb-0">{{ __('auth.forgot_password') }}</h1>
@if ($is_registration_enabled)
<a href="/register" class="flex justify-center pt-2 hover:no-underline">
<button
class="normal-case rounded-none btn btn-sm btn-primary bg-coollabs-gradient">{{ __('auth.register-now') }}</button>
</a>
@endif
</div> </div>
<div class="w-96"> <div class="w-96">
<form action="/login" method="POST" class="flex flex-col gap-2"> <form action="/forgot-password" method="POST" class="flex flex-col gap-2">
@csrf @csrf
@env('local')
<x-forms.input required value="test@example.com" type="email" name="email" <x-forms.input required value="test@example.com" type="email" name="email"
label="{{ __('input.email') }}" autofocus /> label="{{ __('input.email') }}" autofocus />
<x-forms.input required value="password" type="password" name="password" <x-forms.button type="submit">{{ __('auth.forgot_password_send_email') }}</x-forms.button>
label="{{ __('input.password') }}" /> </form>
@else
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" autofocus />
<x-forms.input required type="password" name="password" label="{{ __('input.password') }}" />
@endenv
@if ($errors->any()) @if ($errors->any())
<div class="text-center text-error"> <div class="text-center text-error">
<span>{{ __('auth.failed') }}</span> <span>{{ __('auth.failed') }}</span>
</div> </div>
@endif @endif
<x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button> @if (session('status'))
@if (!$is_registration_enabled) <div class="mb-4 text-sm font-medium text-green-600">
<div class="text-sm text-center">{{ __('auth.registration_disabled') }}</div> {{ session('status') }}
</div>
@endif @endif
</form>
</div> </div>
</div> </div>
</div> --}} </div>
</x-layout-simple> </x-layout-simple>

View File

@@ -1,13 +1,16 @@
<x-layout-simple> <x-layout-simple>
<div class="flex items-center justify-center h-screen"> <div class="flex items-center justify-center h-screen">
<div> <div>
<div class="pb-8 text-5xl font-bold tracking-tight text-center text-white">Coolify</div> <div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
</div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h1 class="pb-0">{{ __('auth.login') }}</h1> <h1 class="pb-0">{{ __('auth.login') }}</h1>
@if ($is_registration_enabled) @if ($is_registration_enabled)
<a href="/register" class="flex justify-center pt-2 hover:no-underline"> <a href="/register" class="flex justify-center pt-2 hover:no-underline">
<button <button
class="normal-case rounded-none btn btn-sm btn-primary bg-coollabs-gradient">{{ __('auth.register-now') }}</button> class="normal-case rounded-none btn btn-sm btn-primary bg-coollabs-gradient">{{ __('auth.register_now') }}</button>
</a> </a>
@endif @endif
</div> </div>
@@ -17,6 +20,17 @@
@env('local') @env('local')
<x-forms.input required value="test@example.com" type="email" name="email" <x-forms.input required value="test@example.com" type="email" name="email"
label="{{ __('input.email') }}" autofocus /> label="{{ __('input.email') }}" autofocus />
{{-- @if (config('mail.default'))
{{ dd('mailer configured') }}
@else
{{ dd('mailer not configured') }}
@endif --}}
@if (!config('mail.default'))
<a href="/forgot-password" class="text-xs">
{{ __('auth.forgot_password') }}?
</a>
@endif
<x-forms.input required value="password" type="password" name="password" <x-forms.input required value="password" type="password" name="password"
label="{{ __('input.password') }}" /> label="{{ __('input.password') }}" />
@else @else
@@ -28,6 +42,11 @@
<span>{{ __('auth.failed') }}</span> <span>{{ __('auth.failed') }}</span>
</div> </div>
@endif @endif
@if (session('status'))
<div class="mb-4 text-sm font-medium text-green-600">
{{ session('status') }}
</div>
@endif
<x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button> <x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button>
@if (!$is_registration_enabled) @if (!$is_registration_enabled)
<div class="text-sm text-center">{{ __('auth.registration_disabled') }}</div> <div class="text-sm text-center">{{ __('auth.registration_disabled') }}</div>

View File

@@ -1,12 +1,15 @@
<x-layout-simple> <x-layout-simple>
<div class="flex items-center justify-center min-h-screen"> <div class="flex items-center justify-center min-h-screen">
<div> <div>
<div class="pb-8 text-5xl font-bold tracking-tight text-center text-white">Coolify</div> <div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
</div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h1 class="pb-0">{{ __('auth.register') }}</h1> <h1 class="pb-0">{{ __('auth.register') }}</h1>
<a href="/login" class="flex justify-center pt-2 hover:no-underline"> <a href="/login" class="flex justify-center pt-2 hover:no-underline">
<button <button
class="normal-case rounded-none btn btn-sm btn-primary bg-coollabs-gradient">{{ __('auth.already-registered') }}</button> class="normal-case rounded-none btn btn-sm btn-primary bg-coollabs-gradient">{{ __('auth.already_registered') }}</button>
</a> </a>
</div> </div>
<form action="/register" method="POST" class="flex flex-col gap-2"> <form action="/register" method="POST" class="flex flex-col gap-2">

View File

@@ -0,0 +1,38 @@
<x-layout-simple>
<div class="flex items-center justify-center h-screen mx-auto">
<div>
<div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
</div>
<div class="flex items-center gap-2">
<h1 class="pb-0">{{ __('auth.reset_password') }}</h1>
</div>
<div>
<form action="/reset-password" method="POST" class="flex flex-col gap-2">
@csrf
<input hidden id="token" name="token" value="{{ request()->route('token') }}">
<input hidden value="{{ request()->query('email') }}" type="email" name="email"
label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input required type="password" id="password" name="password"
label="{{ __('input.password') }}" autofocus />
<x-forms.input required type="password" id="password_confirmation" name="password_confirmation"
label="{{ __('input.password.again') }}" />
</div>
<x-forms.button type="submit">{{ __('auth.reset_password') }}</x-forms.button>
</form>
@if ($errors->any())
<div class="text-center text-error">
<span>{{ __('auth.failed') }}</span>
</div>
@endif
@if (session('status'))
<div class="mb-4 text-sm font-medium text-green-600">
{{ session('status') }}
</div>
@endif
</div>
</div>
</div>
</x-layout-simple>

View File

@@ -0,0 +1,46 @@
<x-layout-simple>
<div class="flex items-center justify-center h-screen">
<div>
<div class="flex flex-col items-center pb-8">
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
<x-version />
</div>
<div class="w-96" x-data="{ showRecovery: false }">
<form action="/two-factor-challenge" method="POST" class="flex flex-col gap-2">
@csrf
<template x-if="!showRecovery">
<div>
<x-forms.input required type="number" name="code" label="{{ __('input.code') }}"
autofocus />
<div class="pt-2 text-xs cursor-pointer hover:underline hover:text-white"
x-on:click="showRecovery = !showRecovery">Use
Recovery Code
</div>
</div>
</template>
<template x-if="showRecovery">
<div>
<x-forms.input required type="text" name="recovery_code "
label="{{ __('input.recovery_code') }}" />
<div class="pt-2 text-xs cursor-pointer hover:underline hover:text-white"
x-on:click="showRecovery = !showRecovery">Use
One-Time Code
</div>
</div>
</template>
<x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button>
</form>
@if ($errors->any())
<div class="text-center text-error">
<span>{{ __('auth.failed') }}</span>
</div>
@endif
@if (session('status'))
<div class="mb-4 text-sm font-medium text-green-600">
{{ session('status') }}
</div>
@endif
</div>
</div>
</div>
</x-layout-simple>

View File

@@ -25,8 +25,7 @@
<main> <main>
{{ $slot }} {{ $slot }}
</main> </main>
<a <x-version class="fixed left-2 bottom-1" />
class="fixed text-xs cursor-pointer left-2 bottom-1 opacity-20 hover:opacity-100 hover:text-white">v{{ config('version') }}</a>
</body> </body>
</html> </html>

View File

@@ -35,8 +35,7 @@
<main> <main>
{{ $slot }} {{ $slot }}
</main> </main>
<a <x-version class="fixed left-2 bottom-1" />
class="fixed text-xs cursor-pointer right-2 bottom-1 opacity-60 hover:opacity-100 hover:text-white">v{{ config('version') }}</a>
@auth @auth
<script> <script>
window.addEventListener("keydown", function(event) { window.addEventListener("keydown", function(event) {

View File

@@ -0,0 +1,2 @@
<a
{{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-20 hover:opacity-100 hover:text-white']) }}>v{{ config('version') }}</a>

View File

@@ -1,23 +1,19 @@
<div class=""> <div>
<div class="text-xl">Discord</div>
<div class="mt-2"></div>
<form wire:submit.prevent='submit' class="flex flex-col"> <form wire:submit.prevent='submit' class="flex flex-col">
<div class="flex flex-col gap-2 xl:flex-row w-96"> <div class="flex items-center gap-2">
<x-inputs.input type="checkbox" id="model.extra_attributes.discord_active" label="Active?" /> <h3>Discord</h3>
<x-forms.button class="w-16 mt-4" type="submit">
Save
</x-forms.button>
</div> </div>
<div class="flex flex-col gap-2 xl:flex-row w-96"> <div class="flex flex-col gap-2 xl:flex-row w-96">
<x-inputs.input id="model.extra_attributes.discord_webhook" label="Discord Webhook" /> <x-forms.checkbox instantSave id="model.smtp_attributes.discord_active" label="Notification Enabled" />
</div>
<div class="flex flex-col gap-2 xl:flex-row w-96">
<x-forms.input required id="model.smtp_attributes.discord_webhook" label="Webhook" />
</div> </div>
<div> <div>
<x-inputs.button class="w-16 mt-4" type="submit">
Submit
</x-inputs.button>
<x-inputs.button
class="mt-4 btn btn-xs no-animation normal-case text-white btn-primary"
wire:click="sendTestNotification"
>
Send test message
</x-inputs.button>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -1,45 +1,35 @@
<div class="mt-10"> <div>
<div class="text-xl">E-mail - SMTP</div> <form wire:submit.prevent='submit' class="flex flex-col mt-2">
<div class="mt-2"></div> <div class="flex items-center gap-2">
<form wire:submit.prevent='submit' class="flex flex-col"> <h3>E-mail (SMTP)</h3>
<x-forms.button class="w-16 mt-4" type="submit">
Save
</x-forms.button>
</div>
<div class="flex flex-col w-96"> <div class="flex flex-col w-96">
<x-inputs.input type="checkbox" id="model.extra_attributes.smtp_active" label="Active?" /> <x-forms.checkbox instantSave id="model.smtp_attributes.smtp_active" label="Notification Enabled" />
</div> </div>
<div class="flex flex-col gap-2 xl:flex-row"> <div class="flex flex-col gap-2 xl:flex-row">
<div class="flex flex-col w-96"> <div class="flex flex-col w-96">
<x-inputs.textarea <x-forms.textarea required id="model.smtp_attributes.recipients" helper="E-mails, one per line"
id="model.extra_attributes.recipients"
helper="E-mails, one per line"
label="Recipients" /> label="Recipients" />
</div> </div>
</div> </div>
<div class="flex flex-col gap-2 xl:flex-row"> <div class="flex flex-col gap-2 xl:flex-row">
<div class="flex flex-col w-96"> <div class="flex flex-col w-96">
<x-inputs.input id="model.extra_attributes.smtp_host" label="Host" /> <x-forms.input required id="model.smtp_attributes.smtp_host" label="Host" />
<x-inputs.input id="model.extra_attributes.smtp_port" label="Port" /> <x-forms.input required id="model.smtp_attributes.smtp_port" label="Port" />
<x-inputs.input id="model.extra_attributes.smtp_encryption" label="Encryption" /> <x-forms.input id="model.smtp_attributes.smtp_encryption" label="Encryption" />
</div> </div>
<div class="flex flex-col w-96"> <div class="flex flex-col w-96">
<x-inputs.input id="model.extra_attributes.smtp_username" label="Username" /> <x-forms.input id="model.smtp_attributes.smtp_username" label="Username" />
<x-inputs.input id="model.extra_attributes.smtp_password" label="Password" /> <x-forms.input id="model.smtp_attributes.smtp_password" label="Password" />
<x-inputs.input id="model.extra_attributes.smtp_timeout" label="Timeout" /> <x-forms.input id="model.smtp_attributes.smtp_timeout" label="Timeout" />
</div> </div>
<div class="flex flex-col w-96"> <div class="flex flex-col w-96">
<x-inputs.input id="model.extra_attributes.from_address" label="From Address" /> <x-forms.input required id="model.smtp_attributes.from_address" label="From Address" />
<x-inputs.input id="model.extra_attributes.from_name" label="From Name" /> <x-forms.input required id="model.smtp_attributes.from_name" label="From Name" />
<x-inputs.input id="model.extra_attributes.test_address" label="Send test e-mails to" />
</div> </div>
</div> </div>
<div class="flex">
<x-inputs.button class="w-16 mt-4" type="submit">
Submit
</x-inputs.button>
<x-inputs.button
class="mt-4 btn btn-xs no-animation normal-case text-white btn-primary"
wire:click="sendTestNotification"
>
Send test message
</x-inputs.button>
</div>
</form> </form>
</div> </div>

View File

@@ -0,0 +1,4 @@
<x-forms.button isHighlighted class="mt-4 text-white normal-case btn btn-xs no-animation btn-primary"
wire:click="sendTestNotification">
Send Test Notifications
</x-forms.button>

View File

@@ -1,10 +1,12 @@
<div> <div>
<form wire:submit.prevent='submit'> <form wire:submit.prevent='submit' class="flex flex-col">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h3>Profile</h3> <h3>General</h3>
<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="flex gap-2">
<x-forms.input id="name" label="Name" required /> <x-forms.input id="name" label="Name" required />
<x-forms.input id="email" label="Email" readonly /> <x-forms.input id="email" label="Email" readonly />
</div>
</form> </form>
</div> </div>

View File

@@ -0,0 +1,18 @@
<form>
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input id="model.extra_attributes.smtp_host" label="Host" />
<x-forms.input id="model.extra_attributes.smtp_port" label="Port" />
<x-forms.input id="model.extra_attributes.smtp_encryption" label="Encryption" />
</div>
<div class="flex gap-2">
<x-forms.input id="model.extra_attributes.smtp_username" label="Username" />
<x-forms.input id="model.extra_attributes.smtp_password" label="Password" />
<x-forms.input id="model.extra_attributes.smtp_timeout" label="Timeout" />
</div>
<div class="flex gap-2">
<x-forms.input id="model.extra_attributes.from_address" label="From Address" />
<x-forms.input id="model.extra_attributes.from_name" label="From Name" />
</div>
</div>
</form>

View File

@@ -6,8 +6,7 @@
Save Save
</x-forms.button> </x-forms.button>
</div> </div>
<div class="pb-4 text-sm">Instance wide settings for Coolify. <div class="pb-4 text-sm">Instance wide settings for Coolify.</div>
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input id="settings.fqdn" label="Coolify's Domain" /> <x-forms.input id="settings.fqdn" label="Coolify's Domain" />
@@ -22,7 +21,6 @@
</div> </div>
</div> </div>
</form> </form>
<h3>Advanced</h3> <h3>Advanced</h3>
<div class="flex flex-col text-right w-52"> <div class="flex flex-col text-right w-52">
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Coolify" /> <x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Coolify" />
@@ -30,7 +28,5 @@
{{-- <x-forms.checkbox instantSave id="is_https_forced" label="Force https?" /> --}} {{-- <x-forms.checkbox instantSave id="is_https_forced" label="Force https?" /> --}}
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" /> <x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div> </div>
@if (auth()->user()->isPartOfRootTeam())
<livewire:force-upgrade />
@endif
</div> </div>

View File

@@ -1,12 +1,9 @@
<div class="pt-4"> <div class="pt-4">
<h3>Switch Team</h3> <h3>Switch Team</h3>
@if (auth()->user()->otherTeams()->count() > 0)
<div class="flex gap-2"> <div class="flex gap-2">
@foreach (auth()->user()->otherTeams() as $team) @foreach (auth()->user()->otherTeams() as $team)
<x-forms.button isHighlighted wire:key="{{ $team->id }}" <x-forms.button isHighlighted wire:key="{{ $team->id }}" wire:click="switch_to('{{ $team->id }}')">
wire:click="switch_to('{{ $team->id }}')">
{{ $team->name }}</x-forms.button> {{ $team->name }}</x-forms.button>
@endforeach @endforeach
</div> </div>
@endif
</div> </div>

View File

@@ -1,3 +1,73 @@
<x-layout> <x-layout>
<livewire:profile.form /> <h1>Profile</h1>
<livewire:profile.form :request="$request" />
<h3>2FA</h3>
@if (session('status') == 'two-factor-authentication-enabled')
<div class="mb-4 text-sm font-medium">
Please finish configuring two factor authentication below. Read the QR code or enter the secret key
manually.
</div>
<div class="flex flex-col gap-2">
<form action="/user/confirmed-two-factor-authentication" method="POST" class="flex items-end w-32 gap-2">
@csrf
<x-forms.input type="number" id="code" label="One-time code" required />
<x-forms.button type="submit">Validate 2FA</x-forms.button>
</form>
<div>
<div>{!! $request->user()->twoFactorQrCodeSvg() !!}</div>
<div x-data="{ showCode: false }">
<x-forms.button x-on:click="showCode = !showCode">Show secret key to manually enter</x-forms.button>
<template x-if="showCode">
<div class="text-sm">{!! decrypt($request->user()->two_factor_secret) !!}</div>
</template>
</div>
</div>
</div>
@elseif(session('status') == 'two-factor-authentication-confirmed')
<div class="mb-4 text-sm">
Two factor authentication confirmed and enabled successfully.
</div>
<div>
<div class="pb-6 text-sm">Here are the recovery codes for your account. Please store them in a secure
location.</div>
<div class="text-white">
@foreach ($request->user()->recoveryCodes() as $code)
<div>{{ $code }}</div>
@endforeach
</div>
</div>
@else
@if ($request->user()->two_factor_confirmed_at)
<div class="text-sm"> Two factor authentication is <span class="text-helper">enabled</span>.</div>
<div class="flex gap-2">
<form action="/user/two-factor-authentication" method="POST">
@csrf
@method ('DELETE')
<x-forms.button type="submit">Disable</x-forms.button>
</form>
<form action="/user/two-factor-recovery-codes" method="POST">
@csrf
<x-forms.button type="submit">Regenerate Recovery Codes</x-forms.button>
</form>
</div>
@if (session('status') == 'recovery-codes-generated')
<div>
<div class="py-6 text-sm">Here are the recovery codes for your account. Please store them in a
secure
location.</div>
<div class="text-white">
@foreach ($request->user()->recoveryCodes() as $code)
<div>{{ $code }}</div>
@endforeach
</div>
</div>
@endif
@else
<div class="text-sm">Two factor authentication is <span class="text-helper">disabled</span>.</div>
<form action="/user/two-factor-authentication" method="POST">
@csrf
<x-forms.button type="submit">Configure 2FA</x-forms.button>
</form>
@endif
@endif
</x-layout> </x-layout>

View File

@@ -1,4 +1,10 @@
<x-layout> <x-layout>
<h1>Settings</h1>
<livewire:settings.form :settings="$settings" /> <livewire:settings.form :settings="$settings" />
@if (auth()->user()->isPartOfRootTeam())
<livewire:force-upgrade />
@endif
<h3 class="pb-0">Notification</h3>
<div class="pb-4 text-sm">Notification (email, discord, etc) settings for Coolify.</div>
<h4>Email</h4>
<livewire:settings.email :model="$settings" />
</x-layout> </x-layout>

View File

@@ -1,13 +1,12 @@
<x-layout> <x-layout>
<div> <h1>Team</h1>
<h3>Current Team</h3> <p>Current Team: {{ session('currentTeam.name') }}</p>
<p>Name: {{ session('currentTeam.name') }}</p> @if (auth()->user()->otherTeams()->count() > 0)
<livewire:switch-team /> <livewire:switch-team />
<div class="h-12"></div> @endif
<h3>Notifications</h3> <h2>Notifications</h2>
<livewire:notifications.discord-settings :model="session('currentTeam')" /> <livewire:notifications.test :model="session('currentTeam')" />
<livewire:notifications.email-settings :model="session('currentTeam')" /> <livewire:notifications.email-settings :model="session('currentTeam')" />
<livewire:notifications.discord-settings :model="session('currentTeam')" />
<div class="h-12"></div> <div class="h-12"></div>
</div>
<livewire:switch-team>
</x-layout> </x-layout>

View File

@@ -125,8 +125,10 @@ Route::middleware(['auth'])->group(function () {
]); ]);
})->name('dashboard'); })->name('dashboard');
Route::get('/profile', function () { Route::get('/profile', function (Request $request) {
return view('profile'); return view('profile', [
'request' => $request,
]);
})->name('profile'); })->name('profile');
Route::get('/profile/team', function () { Route::get('/profile/team', function () {
return view('team'); return view('team');