Merge pull request #1302 from coollabsio/next

v4.0.0-beta.74
This commit is contained in:
Andras Bacsai 2023-10-11 11:09:19 +02:00 committed by GitHub
commit 8b0a0d67da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 316 additions and 193 deletions

View File

@ -36,7 +36,7 @@ # Installation
## Support
Contact us [here](https://coolify.io/contact).
Contact us [here](https://coolify.io/docs/contact).
## Recognitions

View File

@ -32,7 +32,6 @@ protected function schedule(Schedule $schedule): void
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
// $schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
$this->instance_auto_update($schedule);
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);

View File

@ -46,15 +46,6 @@ public function link()
}
return redirect()->route('login')->with('error', 'Invalid credentials.');
}
public function subscription()
{
if (!isCloud()) {
abort(404);
}
return view('subscription.index', [
'settings' => InstanceSettings::get(),
]);
}
public function license()
{

View File

@ -164,7 +164,7 @@ public function saveServer()
{
$this->validate([
'remoteServerName' => 'required',
'remoteServerHost' => 'required',
'remoteServerHost' => 'required|ip',
'remoteServerPort' => 'required|integer',
'remoteServerUser' => 'required',
]);

View File

@ -11,19 +11,11 @@ class Dashboard extends Component
{
public $projects = [];
public $servers = [];
public int $s3s = 0;
public int $resources = 0;
public function mount()
{
$this->servers = Server::ownedByCurrentTeam()->get();
$this->s3s = S3Storage::ownedByCurrentTeam()->get()->count();
$projects = Project::ownedByCurrentTeam()->get();
foreach ($projects as $project) {
$this->resources += $project->applications->count();
$this->resources += $project->postgresqls->count();
}
$this->projects = $projects;
$this->projects = Project::ownedByCurrentTeam()->get();
}
// public function getIptables()
// {

View File

@ -2,12 +2,11 @@
namespace App\Http\Livewire\Project\New;
use App\Models\Project;
use App\Models\Server;
use Countable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Livewire\Component;
class Select extends Component
@ -24,7 +23,8 @@ class Select extends Component
public Collection|array $services = [];
public bool $loadingServices = true;
public bool $loading = false;
public $environments = [];
public ?string $selectedEnvironment = null;
public ?string $existingPostgresqlUrl = null;
protected $queryString = [
@ -37,8 +37,18 @@ public function mount()
if (isDev()) {
$this->existingPostgresqlUrl = 'postgres://coolify:password@coolify-db:5432';
}
$projectUuid = data_get($this->parameters, 'project_uuid');
$this->environments = Project::whereUuid($projectUuid)->first()->environments;
$this->selectedEnvironment = data_get($this->parameters, 'environment_name');
}
public function updatedSelectedEnvironment()
{
return redirect()->route('project.resources.new', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->selectedEnvironment,
]);
}
// public function addExistingPostgresql()
// {
// try {

View File

@ -11,13 +11,13 @@ class ByIp extends Component
{
public $private_keys;
public $limit_reached;
public int|null $private_key_id = null;
public ?int $private_key_id = null;
public $new_private_key_name;
public $new_private_key_description;
public $new_private_key_value;
public string $name;
public string|null $description = null;
public ?string $description = null;
public string $ip;
public string $user = 'root';
public int $port = 22;
@ -26,16 +26,16 @@ class ByIp extends Component
protected $rules = [
'name' => 'required|string',
'description' => 'nullable|string',
'ip' => 'required',
'ip' => 'required|ip',
'user' => 'required|string',
'port' => 'required|integer',
];
protected $validationAttributes = [
'name' => 'name',
'description' => 'description',
'ip' => 'ip',
'user' => 'user',
'port' => 'port',
'name' => 'Name',
'description' => 'Description',
'ip' => 'IP Address',
'user' => 'User',
'port' => 'Port',
];
public function mount()

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Livewire\Server\Proxy;
use App\Models\Server;
use Livewire\Component;
class Logs extends Component
{
public ?Server $server = null;
public $parameters = [];
public function mount()
{
$this->parameters = get_route_parameters();
try {
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.server.proxy.logs');
}
}

View File

@ -9,6 +9,11 @@ class Show extends Component
{
public ?Server $server = null;
public $parameters = [];
protected $listeners = ['proxyStatusUpdated'];
public function proxyStatusUpdated()
{
$this->server->refresh();
}
public function mount()
{
$this->parameters = get_route_parameters();

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Livewire\Subscription;
use App\Models\InstanceSettings;
use Livewire\Component;
class Show extends Component
{
public InstanceSettings $settings;
public bool $alreadySubscribed = false;
public function mount() {
if (!isCloud()) {
return redirect('/');
}
$this->settings = InstanceSettings::get();
$this->alreadySubscribed = currentTeam()->subscription()->exists();
}
public function stripeCustomerPortal() {
$session = getStripeCustomerPortalSession(currentTeam());
if (is_null($session)) {
return;
}
return redirect($session->url);
}
public function render()
{
return view('livewire.subscription.show')->layout('layouts.subscription');
}
}

View File

@ -620,7 +620,7 @@ private function generate_compose_file()
'container_name' => $this->container_name,
'restart' => RESTART_MODE,
'environment' => $environment_variables,
'labels' => generateLabelsApplication($this->application, $this->preview),
'labels' => generateLabelsApplication($this->application, $this->preview, $ports),
'expose' => $ports,
'networks' => [
$this->destination->network,

View File

@ -0,0 +1,27 @@
<?php
namespace App\View\Components\Server;
use App\Models\Server;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class Sidebar extends Component
{
/**
* Create a new component instance.
*/
public function __construct(public Server $server, public $parameters)
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.server.sidebar');
}
}

View File

@ -147,7 +147,7 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
}
return $labels;
}
function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled)
function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
{
$labels = collect([]);
$labels->push('traefik.enable=true');
@ -158,7 +158,9 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled)
$path = $url->getPath();
$schema = $url->getScheme();
$port = $url->getPort();
if (is_null($port) && !is_null($onlyPort)) {
$port = $onlyPort;
}
$http_label = "{$uuid}-http";
$https_label = "{$uuid}-https";
@ -203,9 +205,12 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled)
return $labels;
}
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null, $ports): array
{
$onlyPort = null;
if (count($ports) === 1) {
$onlyPort = $ports[0];
}
$pull_request_id = data_get($preview, 'pull_request_id', 0);
$container_name = generateApplicationContainerName($application, $pull_request_id);
$appId = $application->id;
@ -221,7 +226,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
}
// Add Traefik labels no matter which proxy is selected
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled));
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled,$onlyPort));
}
return $labels->all();
}

View File

@ -102,6 +102,8 @@ function generate_default_proxy_configuration(Server $server)
];
if (isDev()) {
$config['services']['traefik']['command'][] = "--log.level=debug";
$config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
$config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
}
$config = Yaml::dump($config, 4, 2);
SaveConfiguration::run($server, $config);

View File

@ -110,7 +110,10 @@ function getStripeCustomerPortalSession(Team $team)
{
Stripe::setApiKey(config('subscription.stripe_api_key'));
$return_url = route('team.index');
$stripe_customer_id = $team->subscription->stripe_customer_id;
$stripe_customer_id = data_get($team,'subscription.stripe_customer_id');
if (!$stripe_customer_id) {
return null;
}
$session = \Stripe\BillingPortal\Session::create([
'customer' => $stripe_customer_id,
'return_url' => $return_url,

View File

@ -1,7 +1,7 @@
<?php
return [
'docs' => 'https://coolify.io/contact',
'docs' => 'https://coolify.io/docs/contact',
'self_hosted' => env('SELF_HOSTED', true),
'waitlist' => env('WAITLIST', false),
'license_url' => 'https://licenses.coollabs.io',

View File

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

View File

@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.73';
return '4.0.0-beta.74';

View File

@ -34,14 +34,16 @@ services:
POSTGRES_DB: "${DB_DATABASE:-coolify}"
POSTGRES_HOST_AUTH_METHOD: "trust"
volumes:
- coolify-pg-data-dev:/var/lib/postgresql/data
- ./_data/coolify/_volumes/database/:/var/lib/postgresql/data
# - coolify-pg-data-dev:/var/lib/postgresql/data
redis:
ports:
- "${FORWARD_REDIS_PORT:-6379}:6379"
env_file:
- .env
volumes:
- coolify-redis-data-dev:/data
- ./_data/coolify/_volumes/redis/:/data
# - coolify-redis-data-dev:/data
vite:
image: node:19
working_dir: /var/www/html
@ -56,7 +58,8 @@ services:
volumes:
- /:/host
- /var/run/docker.sock:/var/run/docker.sock
- coolify-data-dev:/data/coolify
- ./_data/coolify/:/data/coolify
# - coolify-data-dev:/data/coolify
mailpit:
image: "axllent/mailpit:latest"
container_name: coolify-mail
@ -76,7 +79,8 @@ services:
MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}"
MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}"
volumes:
- coolify-minio-data-dev:/data
- ./_data/coolify/_volumes/minio/:/data
# - coolify-minio-data-dev:/data
networks:
- coolify

View File

@ -0,0 +1,20 @@
<div>
@if ($server->isFunctional())
<div class="flex h-full pr-4">
<div class="flex flex-col gap-4 min-w-fit">
<a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
href="{{ route('server.proxy', $parameters) }}">
<button>Configuration</button>
</a>
@if (data_get($server, 'proxy.type') !== 'NONE')
<a class="{{ request()->routeIs('server.proxy.logs') ? 'text-white' : '' }}"
href="{{ route('server.proxy.logs', $parameters) }}">
<button>Logs</button>
</a>
@endif
</div>
</div>
@else
<div>Server is not validated. Validate first.</div>
@endif
</div>

View File

@ -200,7 +200,7 @@
label="Description" id="remoteServerDescription" />
</div>
<div class="flex gap-2">
<x-forms.input required placeholder="Hostname or IP address" label="Hostname or IP Address"
<x-forms.input required placeholder="127.0.0.1" label="IP Address"
id="remoteServerHost" />
<x-forms.input required placeholder="Port number of your server. Default is 22."
label="Port" id="remoteServerPort" />

View File

@ -14,27 +14,7 @@
<span>Your subscription has been activated! Welcome onboard!</span>
</div>
@endif
<div class="w-full rounded stats stats-vertical lg:stats-horizontal">
<div class="stat">
<div class="stat-title">Servers</div>
<div class="stat-value">{{ $servers->count() }} </div>
</div>
<div class="stat">
<div class="stat-title">Projects</div>
<div class="stat-value">{{ $projects->count() }}</div>
</div>
<div class="stat">
<div class="stat-title">Resources</div>
<div class="stat-value">{{ $resources }}</div>
<div class="stat-desc">Applications, databases, etc...</div>
</div>
<div class="stat">
<div class="stat-title">S3 Storages</div>
<div class="stat-value">{{ $s3s }}</div>
</div>
</div>
<h3 class="pb-4">Projects</h3>
@if ($projects->count() === 1)
@ -60,6 +40,10 @@
{{ $project->description }}</div>
</a>
@endif
<a class="mx-4 rounded group-hover:text-white hover:no-underline "
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
<span class="font-bold hover:text-warning">+ New Resource</span>
</a>
<a class="mx-4 rounded group-hover:text-white"
href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" viewBox="0 0 24 24"
@ -108,11 +92,11 @@
</a>
@endforeach
</div>
<script>
function gotoProject(uuid) {
window.location.href = '/project/' + uuid;
}
</script>
{{-- <x-forms.button wire:click='getIptables'>Get IPTABLES</x-forms.button> --}}
</div>

View File

@ -1,5 +1,14 @@
<div x-data x-init="$wire.loadThings">
<div class="flex gap-2 ">
<h1>New Resource</h1>
<div class="w-96">
<x-forms.select wire:model="selectedEnvironment">
@foreach ($environments as $environment)
<option value="{{ $environment->name }}">Environment: {{ $environment->name }}</option>
@endforeach
</x-forms.select>
</div>
</div>
<div class="pb-4 ">Deploy resources, like Applications, Databases, Services...</div>
<div class="flex flex-col gap-2 pt-10">
@if ($current_step === 'type')

View File

@ -65,8 +65,8 @@
@endif
<div class="text-xs">{{ $application->status }}</div>
</a>
<a class="flex gap-2 p-1 mx-4 text-xs font-bold rounded hover:no-underline hover:text-warning"
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $application->name]) }}">Logs</a>
<a class="flex gap-2 p-1 mx-4 font-bold rounded group-hover:text-white hover:no-underline"
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $application->name]) }}"><span class="hover:text-warning">Logs</span></a>
</div>
@endforeach
@foreach ($databases as $database)
@ -94,8 +94,8 @@
@endif
<div class="text-xs">{{ $database->status }}</div>
</a>
<a class="flex gap-2 p-1 mx-4 text-xs font-bold rounded hover:no-underline hover:text-warning"
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $database->name]) }}">Logs</a>
<a class="flex gap-2 p-1 mx-4 font-bold rounded hover:no-underline group-hover:text-white"
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $database->name]) }}"><span class="hover:text-warning">Logs</span></a>
</div>
@endforeach
</div>

View File

@ -11,7 +11,7 @@
</div>
<div class="flex gap-2">
<x-forms.input id="ip" label="IP Address" required
helper="Could be IP Address (127.0.0.1) or Domain Name (duckduckgo.com)." />
helper="An IP Address (127.0.0.1). No domain names." />
<x-forms.input id="user" label ="User" required />
<x-forms.input type="number" id="port" label="Port" required />
</div>

View File

@ -1,11 +1,10 @@
<div>
@if ($server->isFunctional())
@if (data_get($server, 'proxy.type'))
<div x-init="$wire.loadProxyConfiguration">
@if ($selectedProxy === 'TRAEFIK_V2')
<form wire:submit.prevent='submit'>
<div class="flex items-center gap-2">
<h2>Proxy</h2>
<h2>Configuration</h2>
<x-forms.button type="submit">Save</x-forms.button>
@if ($server->proxy->status === 'exited')
<x-forms.button wire:click.prevent="change_proxy">Switch Proxy</x-forms.button>
@ -38,19 +37,19 @@
</form>
@elseif($selectedProxy === 'NONE')
<div class="flex items-center gap-2">
<h2>Proxy</h2>
<h2>Configuration</h2>
<x-forms.button wire:click.prevent="change_proxy">Switch Proxy</x-forms.button>
</div>
<div class="pt-3 pb-4">None</div>
<div class="pt-3 pb-4">Custom (None) Proxy Selected</div>
@else
<div class="flex items-center gap-2">
<h2>Proxy</h2>
<h2>Configuration</h2>
<x-forms.button wire:click.prevent="change_proxy">Switch Proxy</x-forms.button>
</div>
@endif
@else
<div>
<h2>Proxy</h2>
<h2>Configuration</h2>
<div class="subtitle">Select a proxy you would like to use on this server.</div>
<div class="grid gap-4">
<x-forms.button class="box" wire:click="select_proxy('NONE')">
@ -69,7 +68,4 @@
</div>
</div>
@endif
@else
<div>Server is not validated. Validate first.</div>
@endif
</div>

View File

@ -0,0 +1,9 @@
<div>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />
<div class="w-full">
<livewire:project.shared.get-logs :server="$server" container="coolify-proxy" />
</div>
</div>
</div>

View File

@ -1,4 +1,9 @@
<div>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />
<div class="w-full">
<livewire:server.proxy :server="$server" />
</div>
</div>
</div>

View File

@ -0,0 +1,49 @@
@if ($settings->is_resale_license_active)
@if (auth()->user()->isAdminFromSession())
<div class="flex justify-center mx-10">
<div x-data>
<div class="flex gap-2">
<h1>Subscription</h1>
<livewire:switch-team />
@if (subscriptionProvider() === 'stripe' && $alreadySubscribed)
<x-forms.button wire:click='stripeCustomerPortal'>Manage My Subscription</x-forms.button>
@endif
</div>
<div class="flex items-center pb-8">
<span>Currently active team: <span
class="text-warning">{{ session('currentTeam.name') }}</span></span>
</div>
@if (request()->query->get('cancelled'))
<div class="mb-6 rounded alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Something went wrong with your subscription. Please try again or contact
support.</span>
</div>
@endif
@if (config('subscription.provider') !== null)
<livewire:subscription.pricing-plans />
@endif
</div>
</div>
@else
<div class="flex flex-col justify-center mx-10">
<div class="flex gap-2">
<h1>Subscription</h1>
<livewire:switch-team />
</div>
<div class="flex items-center pb-8">
<span>Currently active team: <span class="text-warning">{{ session('currentTeam.name') }}</span></span>
</div>
<div>You are not an admin or have been removed from this team. If this does not make sense, please <span
class="text-white underline cursor-pointer" wire:click="help" onclick="help.showModal()">contact
us</span>.</div>
</div>
@endif
@else
<div class="px-10">Resale license is not active. Please contact your instance admin.</div>
@endif

View File

@ -1,46 +0,0 @@
<x-layout-subscription>
@if ($settings->is_resale_license_active)
@if (auth()->user()->isAdminFromSession())
<div class="flex justify-center mx-10">
<div x-data>
<div class="flex gap-2">
<h1>Subscription</h1>
<livewire:switch-team />
</div>
<div class="flex items-center pb-8">
<span>Currently active team: <span
class="text-warning">{{ session('currentTeam.name') }}</span></span>
</div>
@if (request()->query->get('cancelled'))
<div class="mb-6 rounded alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Something went wrong with your subscription. Please try again or contact
support.</span>
</div>
@endif
@if (config('subscription.provider') !== null)
<livewire:subscription.pricing-plans />
@endif
</div>
</div>
@else
<div class="flex flex-col justify-center mx-10">
<div class="flex gap-2">
<h1>Subscription</h1>
<livewire:switch-team />
</div>
<div class="flex items-center pb-8">
<span>Currently active team: <span
class="text-warning">{{ session('currentTeam.name') }}</span></span>
</div>
<div>You are not an admin or have been removed from this team. If this does not make sense, please <span class="text-white underline cursor-pointer" wire:click="help" onclick="help.showModal()">contact us</span>.</div>
</div>
@endif
@else
<div class="px-10" >Resale license is not active. Please contact your instance admin.</div>
@endif
</x-layout-subscription>

View File

@ -5,7 +5,6 @@
use App\Http\Controllers\DatabaseController;
use App\Http\Controllers\MagicController;
use App\Http\Controllers\ProjectController;
use App\Http\Controllers\ServerController;
use App\Http\Livewire\Boarding\Index as BoardingIndex;
use App\Http\Livewire\Project\Service\Index as ServiceIndex;
use App\Http\Livewire\Project\Service\Show as ServiceShow;
@ -17,7 +16,9 @@
use App\Http\Livewire\Server\Destination\Show as DestinationShow;
use App\Http\Livewire\Server\PrivateKey\Show as PrivateKeyShow;
use App\Http\Livewire\Server\Proxy\Show as ProxyShow;
use App\Http\Livewire\Server\Proxy\Logs as ProxyLogs;
use App\Http\Livewire\Server\Show;
use App\Http\Livewire\Subscription\Show as SubscriptionShow;
use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
use App\Models\GithubApp;
use App\Models\GitlabApp;
@ -122,6 +123,7 @@
Route::get('/server/new', Create::class)->name('server.create');
Route::get('/server/{server_uuid}', Show::class)->name('server.show');
Route::get('/server/{server_uuid}/proxy', ProxyShow::class)->name('server.proxy');
Route::get('/server/{server_uuid}/proxy/logs', ProxyLogs::class)->name('server.proxy.logs');
Route::get('/server/{server_uuid}/private-key', PrivateKeyShow::class)->name('server.private-key');
Route::get('/server/{server_uuid}/destinations', DestinationShow::class)->name('server.destinations');
});
@ -133,8 +135,7 @@
Route::middleware(['throttle:force-password-reset'])->group(function () {
Route::get('/force-password-reset', [Controller::class, 'force_passoword_reset'])->name('auth.force-password-reset');
});
Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription.index');
// Route::get('/help', Help::class)->name('help');
Route::get('/subscription', SubscriptionShow::class)->name('subscription.index');
Route::get('/settings', [Controller::class, 'settings'])->name('settings.configuration');
Route::get('/settings/license', [Controller::class, 'license'])->name('settings.license');
Route::get('/profile', fn () => view('profile', ['request' => request()]))->name('profile');

View File

@ -4,7 +4,7 @@
"version": "3.12.36"
},
"v4": {
"version": "4.0.0-beta.73"
"version": "4.0.0-beta.74"
}
}
}