Files
coolify/routes/web.php
🏔️ Peak d3f85d777c v4.0.0-beta.420 (#6008)
* chore(version): update coolify-realtime to version 1.0.9 in docker-compose and versions files

* feat(migration): add is_sentinel_enabled column to server_settings with default true

* fix(migration): update default value handling for is_sentinel_enabled column in server_settings

* feat(seeder): dispatch StartProxy action for each server in ProductionSeeder

* feat(seeder): add CheckAndStartSentinelJob dispatch for each server in ProductionSeeder

* fix(seeder): conditionally dispatch CheckAndStartSentinelJob based on server's sentinel status

* feat(seeder): conditionally dispatch StartProxy action based on proxy check result

* refactor(ui): terminal

* refactor(ui): remove terminal header from execute-container-command view

* refactor(ui): remove unnecessary padding from deployment, backup, and logs sections

* fix(service): disable healthcheck logging for Gotenberg (#6005)

* fix(service): Joplin volume name (#5930)

* chore(version): update coolify version to 4.0.0-beta.420 and nightly version to 4.0.0-beta.421

* fix(server): update sentinelUpdatedAt assignment to use server's sentinel_updated_at property

* feat(service): update Changedetection template (#5937)

* chore(service): changedetection remove unused code

* fix(service): audiobookshelf healthcheck command (#5993)

* refactor(service): update Hoarder to their new name karakeep (#5964)

* fix(service): downgrade Evolution API phone version (#5977)

* feat(service): add Miniflux service (#5843)

* refactor(service): karakeep naming and formatting

* refactor(service): improve miniflux

- improve DB url
- add depends_on
- formatting, naming & order

* feat(service): add Pingvin Share service (#5969)

* fix(service): pingvinshare-with-clamav

- add platform to make clamav work
- formatting

* feat(auth): Add Discord OAuth Provider (#5552)

* feat(auth): Add Clerk OAuth Provider (#5553)

* feat(auth): add Zitadel OAuth Provider (#5490)

* Update composer.lock

* fix(ssh): scp requires square brackets for ipv6 (#6001)

* refactor(core): rename API rate limit ENV

* refactor(ui): simplify container selection form in execute-container-command view

* chore(service): Update Evolution API image to the official one (#6031)

* chore(versions): bump coolify versions to v4.0.0-beta.420 and v4.0.0-beta.421

* fix(github): changing github app breaks the webhook. it does not anymore

* feat(service): enhance service status handling and UI updates

* fix(parser): improve FQDN generation and update environment variable handling

* fix(ui):  enhance status refresh buttons with loading indicators

* fix(ui): update confirmation button text for stopping database and service

* fix(routes): update middleware for deploy route to use 'api.ability:deploy'

* fix(ui): refine API token creation form and update helper text for clarity

* fix(ui): adjust layout of deployments section for improved alignment

* chore(dependencies): update composer dependencies to latest versions including resend-laravel to ^0.19.0 and aws-sdk-php to 3.347.0

* refactor(email): streamline SMTP and resend settings logic for improved clarity

* fix(ui): adjust project grid layout and refine server border styling for better visibility

* fix(ui): update border styling for consistency across components and enhance loading indicators

* feat(cleanup): add functionality to delete teams with no members or servers in CleanupStuckedResources command

* refactor(invitation): rename methods for consistency and enhance invitation deletion logic

* refactor(user): streamline user deletion process and enhance team management logic

* fix(ui): add padding to section headers in settings views for improved spacing

* fix(ui): reduce gap between input fields in email settings for better alignment

* fix(docker): conditionally enable gzip compression in Traefik labels based on configuration

* fix(parser): enable gzip compression conditionally for Pocketbase images and streamline service creation logic

* fix(ui): update padding for trademarks policy and enhance spacing in advanced settings section

* feat(ui): add heart icon and enhance popup messaging for sponsorship support

* feat(settings): add sponsorship popup toggle and corresponding database migration

* fix(ui): correct closing tag for sponsorship link in layout popups

* fix(ui): refine wording in sponsorship donation prompt in layout popups

* fix(ui): update navbar icon color and enhance popup layout for sponsorship support

* Update resources/views/livewire/project/shared/health-checks.blade.php

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update app/Livewire/Subscription/Index.php

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix(ui): add target="_blank" to sponsorship links in layout popups for improved user experience

* fix(models): refine comment wording in User model for clarity on user deletion criteria

* Update app/Providers/RouteServiceProvider.php

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix(models): improve user deletion logic in User model to handle team member roles and prevent deletion if user is alone in root team

* fix(ui): update wording in sponsorship prompt for clarity and engagement

---------

Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Co-authored-by: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com>
Co-authored-by: Carsten <BanditsBacon@users.noreply.github.com>
Co-authored-by: Alberto Rizzi <48057685+albertorizzi@users.noreply.github.com>
Co-authored-by: Jonas Klesen <deklesen@gmail.com>
Co-authored-by: Stew Night. <22344601+stewnight@users.noreply.github.com>
Co-authored-by: Jeffer Marcelino <jeffersunde72@gmail.com>
Co-authored-by: Lucas Eduardo <lucas59356@gmail.com>
Co-authored-by: CrazyTim71 <118295691+CrazyTim71@users.noreply.github.com>
Co-authored-by: Yassir Elmarissi <yassir.elmarissi@hm.edu>
Co-authored-by: Hauke Schnau <hauke@schnau-lilienthal.de>
Co-authored-by: Darren Sisson <74752850+djsisson@users.noreply.github.com>
Co-authored-by: Alkesh Das <67038642+smad-bro@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-06-26 12:23:08 +02:00

368 lines
21 KiB
PHP

<?php
use App\Http\Controllers\Controller;
use App\Http\Controllers\OauthController;
use App\Http\Controllers\UploadController;
use App\Livewire\Admin\Index as AdminIndex;
use App\Livewire\Boarding\Index as BoardingIndex;
use App\Livewire\Dashboard;
use App\Livewire\Destination\Index as DestinationIndex;
use App\Livewire\Destination\Show as DestinationShow;
use App\Livewire\ForcePasswordReset;
use App\Livewire\Notifications\Discord as NotificationDiscord;
use App\Livewire\Notifications\Email as NotificationEmail;
use App\Livewire\Notifications\Pushover as NotificationPushover;
use App\Livewire\Notifications\Slack as NotificationSlack;
use App\Livewire\Notifications\Telegram as NotificationTelegram;
use App\Livewire\Profile\Index as ProfileIndex;
use App\Livewire\Project\Application\Configuration as ApplicationConfiguration;
use App\Livewire\Project\Application\Deployment\Index as DeploymentIndex;
use App\Livewire\Project\Application\Deployment\Show as DeploymentShow;
use App\Livewire\Project\CloneMe as ProjectCloneMe;
use App\Livewire\Project\Database\Backup\Execution as DatabaseBackupExecution;
use App\Livewire\Project\Database\Backup\Index as DatabaseBackupIndex;
use App\Livewire\Project\Database\Configuration as DatabaseConfiguration;
use App\Livewire\Project\Edit as ProjectEdit;
use App\Livewire\Project\EnvironmentEdit;
use App\Livewire\Project\Index as ProjectIndex;
use App\Livewire\Project\Resource\Create as ResourceCreate;
use App\Livewire\Project\Resource\Index as ResourceIndex;
use App\Livewire\Project\Service\Configuration as ServiceConfiguration;
use App\Livewire\Project\Service\Index as ServiceIndex;
use App\Livewire\Project\Shared\ExecuteContainerCommand;
use App\Livewire\Project\Shared\Logs;
use App\Livewire\Project\Shared\ScheduledTask\Show as ScheduledTaskShow;
use App\Livewire\Project\Show as ProjectShow;
use App\Livewire\Security\ApiTokens;
use App\Livewire\Security\PrivateKey\Index as SecurityPrivateKeyIndex;
use App\Livewire\Security\PrivateKey\Show as SecurityPrivateKeyShow;
use App\Livewire\Server\Advanced as ServerAdvanced;
use App\Livewire\Server\CaCertificate\Show as CaCertificateShow;
use App\Livewire\Server\Charts as ServerCharts;
use App\Livewire\Server\CloudflareTunnel;
use App\Livewire\Server\Delete as DeleteServer;
use App\Livewire\Server\Destinations as ServerDestinations;
use App\Livewire\Server\DockerCleanup;
use App\Livewire\Server\Index as ServerIndex;
use App\Livewire\Server\LogDrains;
use App\Livewire\Server\PrivateKey\Show as PrivateKeyShow;
use App\Livewire\Server\Proxy\DynamicConfigurations as ProxyDynamicConfigurations;
use App\Livewire\Server\Proxy\Logs as ProxyLogs;
use App\Livewire\Server\Proxy\Show as ProxyShow;
use App\Livewire\Server\Resources as ResourcesShow;
use App\Livewire\Server\Security\Patches;
use App\Livewire\Server\Show as ServerShow;
use App\Livewire\Settings\Index as SettingsIndex;
use App\Livewire\SettingsBackup;
use App\Livewire\SettingsEmail;
use App\Livewire\SettingsOauth;
use App\Livewire\SharedVariables\Environment\Index as EnvironmentSharedVariablesIndex;
use App\Livewire\SharedVariables\Environment\Show as EnvironmentSharedVariablesShow;
use App\Livewire\SharedVariables\Index as SharedVariablesIndex;
use App\Livewire\SharedVariables\Project\Index as ProjectSharedVariablesIndex;
use App\Livewire\SharedVariables\Project\Show as ProjectSharedVariablesShow;
use App\Livewire\SharedVariables\Team\Index as TeamSharedVariablesIndex;
use App\Livewire\Source\Github\Change as GitHubChange;
use App\Livewire\Storage\Index as StorageIndex;
use App\Livewire\Storage\Show as StorageShow;
use App\Livewire\Subscription\Index as SubscriptionIndex;
use App\Livewire\Subscription\Show as SubscriptionShow;
use App\Livewire\Tags\Show as TagsShow;
use App\Livewire\Team\AdminView as TeamAdminView;
use App\Livewire\Team\Index as TeamIndex;
use App\Livewire\Team\Member\Index as TeamMemberIndex;
use App\Livewire\Terminal\Index as TerminalIndex;
use App\Models\GitlabApp;
use App\Models\ScheduledDatabaseBackupExecution;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot')->middleware('throttle:forgot-password');
Route::get('/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
Route::get('/verify', [Controller::class, 'verify'])->middleware('auth')->name('verify.email');
Route::get('/email/verify/{id}/{hash}', [Controller::class, 'email_verify'])->middleware(['auth'])->name('verify.verify');
Route::middleware(['throttle:login'])->group(function () {
Route::get('/auth/link', [Controller::class, 'link'])->name('auth.link');
});
Route::get('/auth/{provider}/redirect', [OauthController::class, 'redirect'])->name('auth.redirect');
Route::get('/auth/{provider}/callback', [OauthController::class, 'callback'])->name('auth.callback');
Route::middleware(['auth', 'verified'])->group(function () {
Route::middleware(['throttle:force-password-reset'])->group(function () {
Route::get('/force-password-reset', ForcePasswordReset::class)->name('auth.force-password-reset');
});
Route::get('/', Dashboard::class)->name('dashboard');
Route::get('/onboarding', BoardingIndex::class)->name('onboarding');
Route::get('/subscription', SubscriptionShow::class)->name('subscription.show');
Route::get('/subscription/new', SubscriptionIndex::class)->name('subscription.index');
Route::get('/settings', SettingsIndex::class)->name('settings.index');
Route::get('/settings/backup', SettingsBackup::class)->name('settings.backup');
Route::get('/settings/email', SettingsEmail::class)->name('settings.email');
Route::get('/settings/oauth', SettingsOauth::class)->name('settings.oauth');
Route::get('/profile', ProfileIndex::class)->name('profile');
Route::prefix('tags')->group(function () {
Route::get('/{tagName?}', TagsShow::class)->name('tags.show');
});
Route::prefix('notifications')->group(function () {
Route::get('/email', NotificationEmail::class)->name('notifications.email');
Route::get('/telegram', NotificationTelegram::class)->name('notifications.telegram');
Route::get('/discord', NotificationDiscord::class)->name('notifications.discord');
Route::get('/slack', NotificationSlack::class)->name('notifications.slack');
Route::get('/pushover', NotificationPushover::class)->name('notifications.pushover');
});
Route::prefix('storages')->group(function () {
Route::get('/', StorageIndex::class)->name('storage.index');
Route::get('/{storage_uuid}', StorageShow::class)->name('storage.show');
});
Route::prefix('shared-variables')->group(function () {
Route::get('/', SharedVariablesIndex::class)->name('shared-variables.index');
Route::get('/team', TeamSharedVariablesIndex::class)->name('shared-variables.team.index');
Route::get('/projects', ProjectSharedVariablesIndex::class)->name('shared-variables.project.index');
Route::get('/project/{project_uuid}', ProjectSharedVariablesShow::class)->name('shared-variables.project.show');
Route::get('/environments', EnvironmentSharedVariablesIndex::class)->name('shared-variables.environment.index');
Route::get('/environments/project/{project_uuid}/environment/{environment_uuid}', EnvironmentSharedVariablesShow::class)->name('shared-variables.environment.show');
});
Route::prefix('team')->group(function () {
Route::get('/', TeamIndex::class)->name('team.index');
Route::get('/members', TeamMemberIndex::class)->name('team.member.index');
Route::get('/admin', TeamAdminView::class)->name('team.admin-view');
});
Route::get('/terminal', TerminalIndex::class)->name('terminal');
Route::post('/terminal/auth', function () {
if (auth()->check()) {
return response()->json(['authenticated' => true], 200);
}
return response()->json(['authenticated' => false], 401);
})->name('terminal.auth');
Route::post('/terminal/auth/ips', function () {
if (auth()->check()) {
$team = auth()->user()->currentTeam();
$ipAddresses = $team->servers->where('settings.is_terminal_enabled', true)->pluck('ip')->toArray();
return response()->json(['ipAddresses' => $ipAddresses], 200);
}
return response()->json(['ipAddresses' => []], 401);
})->name('terminal.auth.ips');
Route::prefix('invitations')->group(function () {
Route::get('/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept');
Route::get('/{uuid}/revoke', [Controller::class, 'revokeInvitation'])->name('team.invitation.revoke');
});
Route::get('/projects', ProjectIndex::class)->name('project.index');
Route::prefix('project/{project_uuid}')->group(function () {
Route::get('/', ProjectShow::class)->name('project.show');
Route::get('/edit', ProjectEdit::class)->name('project.edit');
});
Route::prefix('project/{project_uuid}/environment/{environment_uuid}')->group(function () {
Route::get('/', ResourceIndex::class)->name('project.resource.index');
Route::get('/clone', ProjectCloneMe::class)->name('project.clone-me');
Route::get('/new', ResourceCreate::class)->name('project.resource.create');
Route::get('/edit', EnvironmentEdit::class)->name('project.environment.edit');
});
Route::prefix('project/{project_uuid}/environment/{environment_uuid}/application/{application_uuid}')->group(function () {
Route::get('/', ApplicationConfiguration::class)->name('project.application.configuration');
Route::get('/swarm', ApplicationConfiguration::class)->name('project.application.swarm');
Route::get('/advanced', ApplicationConfiguration::class)->name('project.application.advanced');
Route::get('/environment-variables', ApplicationConfiguration::class)->name('project.application.environment-variables');
Route::get('/persistent-storage', ApplicationConfiguration::class)->name('project.application.persistent-storage');
Route::get('/source', ApplicationConfiguration::class)->name('project.application.source');
Route::get('/servers', ApplicationConfiguration::class)->name('project.application.servers');
Route::get('/scheduled-tasks', ApplicationConfiguration::class)->name('project.application.scheduled-tasks.show');
Route::get('/webhooks', ApplicationConfiguration::class)->name('project.application.webhooks');
Route::get('/preview-deployments', ApplicationConfiguration::class)->name('project.application.preview-deployments');
Route::get('/healthcheck', ApplicationConfiguration::class)->name('project.application.healthcheck');
Route::get('/rollback', ApplicationConfiguration::class)->name('project.application.rollback');
Route::get('/resource-limits', ApplicationConfiguration::class)->name('project.application.resource-limits');
Route::get('/resource-operations', ApplicationConfiguration::class)->name('project.application.resource-operations');
Route::get('/metrics', ApplicationConfiguration::class)->name('project.application.metrics');
Route::get('/tags', ApplicationConfiguration::class)->name('project.application.tags');
Route::get('/danger', ApplicationConfiguration::class)->name('project.application.danger');
Route::get('/deployment', DeploymentIndex::class)->name('project.application.deployment.index');
Route::get('/deployment/{deployment_uuid}', DeploymentShow::class)->name('project.application.deployment.show');
Route::get('/logs', Logs::class)->name('project.application.logs');
Route::get('/terminal', ExecuteContainerCommand::class)->name('project.application.command');
Route::get('/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.application.scheduled-tasks');
});
Route::prefix('project/{project_uuid}/environment/{environment_uuid}/database/{database_uuid}')->group(function () {
Route::get('/', DatabaseConfiguration::class)->name('project.database.configuration');
Route::get('/environment-variables', DatabaseConfiguration::class)->name('project.database.environment-variables');
Route::get('/servers', DatabaseConfiguration::class)->name('project.database.servers');
Route::get('/import-backups', DatabaseConfiguration::class)->name('project.database.import-backups');
Route::get('/persistent-storage', DatabaseConfiguration::class)->name('project.database.persistent-storage');
Route::get('/webhooks', DatabaseConfiguration::class)->name('project.database.webhooks');
Route::get('/resource-limits', DatabaseConfiguration::class)->name('project.database.resource-limits');
Route::get('/resource-operations', DatabaseConfiguration::class)->name('project.database.resource-operations');
Route::get('/metrics', DatabaseConfiguration::class)->name('project.database.metrics');
Route::get('/tags', DatabaseConfiguration::class)->name('project.database.tags');
Route::get('/danger', DatabaseConfiguration::class)->name('project.database.danger');
Route::get('/logs', Logs::class)->name('project.database.logs');
Route::get('/terminal', ExecuteContainerCommand::class)->name('project.database.command');
Route::get('/backups', DatabaseBackupIndex::class)->name('project.database.backup.index');
Route::get('/backups/{backup_uuid}', DatabaseBackupExecution::class)->name('project.database.backup.execution');
});
Route::prefix('project/{project_uuid}/environment/{environment_uuid}/service/{service_uuid}')->group(function () {
Route::get('/', ServiceConfiguration::class)->name('project.service.configuration');
Route::get('/logs', Logs::class)->name('project.service.logs');
Route::get('/environment-variables', ServiceConfiguration::class)->name('project.service.environment-variables');
Route::get('/storages', ServiceConfiguration::class)->name('project.service.storages');
Route::get('/scheduled-tasks', ServiceConfiguration::class)->name('project.service.scheduled-tasks.show');
Route::get('/webhooks', ServiceConfiguration::class)->name('project.service.webhooks');
Route::get('/resource-operations', ServiceConfiguration::class)->name('project.service.resource-operations');
Route::get('/tags', ServiceConfiguration::class)->name('project.service.tags');
Route::get('/danger', ServiceConfiguration::class)->name('project.service.danger');
Route::get('/terminal', ExecuteContainerCommand::class)->name('project.service.command');
Route::get('/{stack_service_uuid}', ServiceIndex::class)->name('project.service.index');
Route::get('/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.service.scheduled-tasks');
});
Route::get('/servers', ServerIndex::class)->name('server.index');
// Route::get('/server/new', ServerCreate::class)->name('server.create');
Route::prefix('server/{server_uuid}')->group(function () {
Route::get('/', ServerShow::class)->name('server.show');
Route::get('/advanced', ServerAdvanced::class)->name('server.advanced');
Route::get('/private-key', PrivateKeyShow::class)->name('server.private-key');
Route::get('/ca-certificate', CaCertificateShow::class)->name('server.ca-certificate');
Route::get('/resources', ResourcesShow::class)->name('server.resources');
Route::get('/cloudflare-tunnel', CloudflareTunnel::class)->name('server.cloudflare-tunnel');
Route::get('/destinations', ServerDestinations::class)->name('server.destinations');
Route::get('/log-drains', LogDrains::class)->name('server.log-drains');
Route::get('/metrics', ServerCharts::class)->name('server.charts');
Route::get('/danger', DeleteServer::class)->name('server.delete');
Route::get('/proxy', ProxyShow::class)->name('server.proxy');
Route::get('/proxy/dynamic', ProxyDynamicConfigurations::class)->name('server.proxy.dynamic-confs');
Route::get('/proxy/logs', ProxyLogs::class)->name('server.proxy.logs');
Route::get('/terminal', ExecuteContainerCommand::class)->name('server.command');
Route::get('/docker-cleanup', DockerCleanup::class)->name('server.docker-cleanup');
Route::get('/security', fn () => redirect(route('dashboard')))->name('server.security');
Route::get('/security/patches', Patches::class)->name('server.security.patches');
});
Route::get('/destinations', DestinationIndex::class)->name('destination.index');
Route::get('/destination/{destination_uuid}', DestinationShow::class)->name('destination.show');
// Route::get('/security', fn () => view('security.index'))->name('security.index');
Route::get('/security/private-key', SecurityPrivateKeyIndex::class)->name('security.private-key.index');
// Route::get('/security/private-key/new', SecurityPrivateKeyCreate::class)->name('security.private-key.create');
Route::get('/security/private-key/{private_key_uuid}', SecurityPrivateKeyShow::class)->name('security.private-key.show');
Route::get('/security/api-tokens', ApiTokens::class)->name('security.api-tokens');
});
Route::middleware(['auth'])->group(function () {
Route::get('/sources', function () {
$sources = currentTeam()->sources();
return view('source.all', [
'sources' => $sources,
]);
})->name('source.all');
Route::get('/source/github/{github_app_uuid}', GitHubChange::class)->name('source.github.show');
Route::get('/source/gitlab/{gitlab_app_uuid}', function (Request $request) {
$gitlab_app = GitlabApp::ownedByCurrentTeam()->where('uuid', request()->gitlab_app_uuid)->firstOrFail();
return view('source.gitlab.show', [
'gitlab_app' => $gitlab_app,
]);
})->name('source.gitlab.show');
});
Route::middleware(['auth'])->group(function () {
Route::post('/upload/backup/{databaseUuid}', [UploadController::class, 'upload'])->name('upload.backup');
Route::get('/download/backup/{executionId}', function () {
try {
$user = auth()->user();
$team = $user->currentTeam();
if (is_null($team)) {
return response()->json(['message' => 'Team not found.'], 404);
}
if ($user->isAdminFromSession() === false) {
return response()->json(['message' => 'Only team admins/owners can download backups.'], 403);
}
$exeuctionId = request()->route('executionId');
$execution = ScheduledDatabaseBackupExecution::where('id', $exeuctionId)->firstOrFail();
$execution_team_id = $execution->scheduledDatabaseBackup->database->team()?->id;
if ($team->id !== 0) {
if (is_null($execution_team_id)) {
return response()->json(['message' => 'Team not found.'], 404);
}
if ($team->id !== $execution_team_id) {
return response()->json(['message' => 'Permission denied.'], 403);
}
if (is_null($execution)) {
return response()->json(['message' => 'Backup not found.'], 404);
}
}
$filename = data_get($execution, 'filename');
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === \App\Models\ServiceDatabase::class) {
$server = $execution->scheduledDatabaseBackup->database->service->destination->server;
} else {
$server = $execution->scheduledDatabaseBackup->database->destination->server;
}
$privateKeyLocation = $server->privateKey->getKeyLocation();
$disk = Storage::build([
'driver' => 'sftp',
'host' => $server->ip,
'port' => (int) $server->port,
'username' => $server->user,
'privateKey' => $privateKeyLocation,
'root' => '/',
]);
if (! $disk->exists($filename)) {
return response()->json(['message' => 'Backup not found.'], 404);
}
return new StreamedResponse(function () use ($disk, $filename) {
if (ob_get_level()) {
ob_end_clean();
}
$stream = $disk->readStream($filename);
if ($stream === false || is_null($stream)) {
abort(500, 'Failed to open stream for the requested file.');
}
while (! feof($stream)) {
echo fread($stream, 2048);
flush();
}
fclose($stream);
}, 200, [
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename="'.basename($filename).'"',
]);
} catch (\Throwable $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
})->name('download.backup');
});
Route::any('/{any}', function () {
if (auth()->user()) {
return redirect(RouteServiceProvider::HOME);
}
return redirect()->route('login');
})->where('any', '.*');