mirror of
https://github.com/pelican-dev/panel.git
synced 2026-02-22 19:08:44 +03:00
Compare commits
5 Commits
lance/769
...
lance/1022
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea978f3485 | ||
|
|
c8dcff2ee9 | ||
|
|
0579fd6df5 | ||
|
|
789c4c7284 | ||
|
|
b1a39f1724 |
@@ -2,11 +2,18 @@
|
||||
|
||||
namespace App\Extensions\Filesystem;
|
||||
|
||||
use Aws\CommandInterface;
|
||||
use Aws\Result;
|
||||
use Aws\S3\S3ClientInterface;
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
|
||||
use RuntimeException;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class S3Filesystem extends AwsS3V3Adapter
|
||||
{
|
||||
private ?GuzzleClient $guzzle = null;
|
||||
|
||||
/**
|
||||
* @param array<mixed> $options
|
||||
*/
|
||||
@@ -26,6 +33,18 @@ class S3Filesystem extends AwsS3V3Adapter
|
||||
);
|
||||
}
|
||||
|
||||
private function getGuzzleClient(): GuzzleClient
|
||||
{
|
||||
if ($this->guzzle === null) {
|
||||
$this->guzzle = new GuzzleClient([
|
||||
'timeout' => 30,
|
||||
'connect_timeout' => 10,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->guzzle;
|
||||
}
|
||||
|
||||
public function getClient(): S3ClientInterface
|
||||
{
|
||||
return $this->client;
|
||||
@@ -35,4 +54,78 @@ class S3Filesystem extends AwsS3V3Adapter
|
||||
{
|
||||
return $this->bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an S3 command using a presigned URL for maximum compatibility
|
||||
* with S3-compatible providers.
|
||||
*
|
||||
* @return Result<array<string, mixed>>
|
||||
*/
|
||||
public function executeS3Command(CommandInterface $command): Result
|
||||
{
|
||||
$presignedRequest = $this->client->createPresignedRequest($command, '+60 minutes');
|
||||
|
||||
$response = $this->getGuzzleClient()->send($presignedRequest);
|
||||
|
||||
$body = (string) $response->getBody();
|
||||
$commandName = $command->getName();
|
||||
|
||||
// S3's CompleteMultipartUpload can return HTTP 200 with an <Error> body
|
||||
if ($body !== '' && str_contains($body, '<Error>')) {
|
||||
throw new RuntimeException("S3 returned an error for $commandName: $body");
|
||||
}
|
||||
|
||||
return new Result($this->parseS3Response($commandName, $body));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the XML response body based on the S3 command type.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function parseS3Response(string $commandName, string $body): array
|
||||
{
|
||||
if ($body === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$xml = @simplexml_load_string($body);
|
||||
if ($xml === false) {
|
||||
throw new RuntimeException("Failed to parse S3 XML response for $commandName: $body");
|
||||
}
|
||||
|
||||
return match ($commandName) {
|
||||
'CreateMultipartUpload' => $this->parseCreateMultipartUpload($xml),
|
||||
'ListParts' => $this->parseListParts($xml),
|
||||
'CompleteMultipartUpload' => [],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{UploadId: string}
|
||||
*/
|
||||
private function parseCreateMultipartUpload(SimpleXMLElement $xml): array
|
||||
{
|
||||
return [
|
||||
'UploadId' => (string) $xml->UploadId,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{Parts: array<int, array{ETag: string, PartNumber: int}>}
|
||||
*/
|
||||
private function parseListParts(SimpleXMLElement $xml): array
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
foreach ($xml->Part as $part) {
|
||||
$parts[] = [
|
||||
'ETag' => (string) $part->ETag,
|
||||
'PartNumber' => (int) $part->PartNumber,
|
||||
];
|
||||
}
|
||||
|
||||
return ['Parts' => $parts];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class BackupRemoteUploadController extends Controller
|
||||
}
|
||||
|
||||
// Execute the CreateMultipartUpload request
|
||||
$result = $client->execute($client->getCommand('CreateMultipartUpload', $params));
|
||||
$result = $adapter->executeS3Command($client->getCommand('CreateMultipartUpload', $params));
|
||||
|
||||
// Get the UploadId from the CreateMultipartUpload request, this is needed to create
|
||||
// the other presigned urls.
|
||||
|
||||
@@ -138,7 +138,7 @@ class BackupStatusController extends Controller
|
||||
|
||||
$client = $adapter->getClient();
|
||||
if (!$successful) {
|
||||
$client->execute($client->getCommand('AbortMultipartUpload', $params));
|
||||
$adapter->executeS3Command($client->getCommand('AbortMultipartUpload', $params));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ class BackupStatusController extends Controller
|
||||
];
|
||||
|
||||
if (is_null($parts)) {
|
||||
$params['MultipartUpload']['Parts'] = $client->execute($client->getCommand('ListParts', $params))['Parts'];
|
||||
$params['MultipartUpload']['Parts'] = $adapter->executeS3Command($client->getCommand('ListParts', $params))['Parts'];
|
||||
} else {
|
||||
foreach ($parts as $part) {
|
||||
$params['MultipartUpload']['Parts'][] = [
|
||||
@@ -159,6 +159,6 @@ class BackupStatusController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$client->execute($client->getCommand('CompleteMultipartUpload', $params));
|
||||
$adapter->executeS3Command($client->getCommand('CompleteMultipartUpload', $params));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,16 @@ class AccountCreated extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(User $notifiable): MailMessage
|
||||
{
|
||||
$locale = $notifiable->language ?? 'en';
|
||||
|
||||
$message = (new MailMessage())
|
||||
->greeting('Hello ' . $notifiable->username . '!')
|
||||
->line('You are receiving this email because an account has been created for you on ' . config('app.name') . '.')
|
||||
->line('Username: ' . $notifiable->username)
|
||||
->line('Email: ' . $notifiable->email);
|
||||
->greeting(trans('mail.greeting', ['name' => $notifiable->username], $locale))
|
||||
->line(trans('mail.account_created.body', ['app' => config('app.name')], $locale))
|
||||
->line(trans('mail.account_created.username', ['username' => $notifiable->username], $locale))
|
||||
->line(trans('mail.account_created.email', ['email' => $notifiable->email], $locale));
|
||||
|
||||
if (!is_null($this->token)) {
|
||||
return $message->action('Setup Your Account', Filament::getPanel('app')->getResetPasswordUrl($this->token, $notifiable));
|
||||
return $message->action(trans('mail.account_created.action', locale: $locale), Filament::getPanel('app')->getResetPasswordUrl($this->token, $notifiable));
|
||||
}
|
||||
|
||||
return $message;
|
||||
|
||||
@@ -24,10 +24,12 @@ class AddedToServer extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(User $notifiable): MailMessage
|
||||
{
|
||||
$locale = $notifiable->language ?? 'en';
|
||||
|
||||
return (new MailMessage())
|
||||
->greeting('Hello ' . $notifiable->username . '!')
|
||||
->line('You have been added as a subuser for the following server, allowing you certain control over the server.')
|
||||
->line('Server Name: ' . $this->server->name)
|
||||
->action('Visit Server', Console::getUrl(panel: 'server', tenant: $this->server));
|
||||
->greeting(trans('mail.greeting', ['name' => $notifiable->username], $locale))
|
||||
->line(trans('mail.added_to_server.body', locale: $locale))
|
||||
->line(trans('mail.added_to_server.server_name', ['name' => $this->server->name], $locale))
|
||||
->action(trans('mail.added_to_server.action', locale: $locale), Console::getUrl(panel: 'server', tenant: $this->server));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@ class MailTested extends Notification
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$locale = $this->user->language ?? 'en';
|
||||
|
||||
return (new MailMessage())
|
||||
->subject('Panel Test Message')
|
||||
->greeting('Hello ' . $this->user->username . '!')
|
||||
->line('This is a test of the Panel mail system. You\'re good to go!');
|
||||
->subject(trans('mail.mail_tested.subject', locale: $locale))
|
||||
->greeting(trans('mail.greeting', ['name' => $this->user->username], $locale))
|
||||
->line(trans('mail.mail_tested.body', locale: $locale));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,13 @@ class RemovedFromServer extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(User $notifiable): MailMessage
|
||||
{
|
||||
$locale = $notifiable->language ?? 'en';
|
||||
|
||||
return (new MailMessage())
|
||||
->error()
|
||||
->greeting('Hello ' . $notifiable->username . '.')
|
||||
->line('You have been removed as a subuser for the following server.')
|
||||
->line('Server Name: ' . $this->server->name)
|
||||
->action('Visit Panel', url(''));
|
||||
->greeting(trans('mail.greeting', ['name' => $notifiable->username], $locale))
|
||||
->line(trans('mail.removed_from_server.body', locale: $locale))
|
||||
->line(trans('mail.removed_from_server.server_name', ['name' => $this->server->name], $locale))
|
||||
->action(trans('mail.removed_from_server.action', locale: $locale), url(''));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,12 @@ class ServerInstalled extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(User $notifiable): MailMessage
|
||||
{
|
||||
$locale = $notifiable->language ?? 'en';
|
||||
|
||||
return (new MailMessage())
|
||||
->greeting('Hello ' . $notifiable->username . '.')
|
||||
->line('Your server has finished installing and is now ready for you to use.')
|
||||
->line('Server Name: ' . $this->server->name)
|
||||
->action('Login and Begin Using', Console::getUrl(panel: 'server', tenant: $this->server));
|
||||
->greeting(trans('mail.greeting', ['name' => $notifiable->username], $locale))
|
||||
->line(trans('mail.server_installed.body', locale: $locale))
|
||||
->line(trans('mail.server_installed.server_name', ['name' => $this->server->name], $locale))
|
||||
->action(trans('mail.server_installed.action', locale: $locale), Console::getUrl(panel: 'server', tenant: $this->server));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use Filament\Facades\Filament;
|
||||
use Illuminate\Contracts\Auth\Factory as AuthFactory;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Throwable;
|
||||
@@ -71,7 +70,7 @@ class ActivityLogService
|
||||
*/
|
||||
public function subject(...$subjects): self
|
||||
{
|
||||
foreach (Arr::wrap($subjects) as $subject) {
|
||||
foreach ($subjects as $subject) {
|
||||
if (is_null($subject)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Extensions\Backups\BackupManager;
|
||||
use App\Extensions\Filesystem\S3Filesystem;
|
||||
use App\Models\Backup;
|
||||
use App\Repositories\Daemon\DaemonBackupRepository;
|
||||
use Aws\S3\S3Client;
|
||||
use Exception;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Http\Response;
|
||||
@@ -72,14 +71,12 @@ class DeleteBackupService
|
||||
|
||||
/** @var S3Filesystem $adapter */
|
||||
$adapter = $this->manager->adapter(Backup::ADAPTER_AWS_S3);
|
||||
|
||||
/** @var S3Client $client */
|
||||
$client = $adapter->getClient();
|
||||
|
||||
$client->deleteObject([
|
||||
$adapter->executeS3Command($client->getCommand('DeleteObject', [
|
||||
'Bucket' => $adapter->getBucket(),
|
||||
'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid),
|
||||
]);
|
||||
]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
35
lang/en/mail.php
Normal file
35
lang/en/mail.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'greeting' => 'Hello :name!',
|
||||
|
||||
'account_created' => [
|
||||
'body' => 'You are receiving this email because an account has been created for you on :app.',
|
||||
'username' => 'Username: :username',
|
||||
'email' => 'Email: :email',
|
||||
'action' => 'Setup Your Account',
|
||||
],
|
||||
|
||||
'added_to_server' => [
|
||||
'body' => 'You have been added as a subuser for the following server, allowing you certain control over the server.',
|
||||
'server_name' => 'Server Name: :name',
|
||||
'action' => 'Visit Server',
|
||||
],
|
||||
|
||||
'removed_from_server' => [
|
||||
'body' => 'You have been removed as a subuser for the following server.',
|
||||
'server_name' => 'Server Name: :name',
|
||||
'action' => 'Visit Panel',
|
||||
],
|
||||
|
||||
'server_installed' => [
|
||||
'body' => 'Your server has finished installing and is now ready for you to use.',
|
||||
'server_name' => 'Server Name: :name',
|
||||
'action' => 'Login and Begin Using',
|
||||
],
|
||||
|
||||
'mail_tested' => [
|
||||
'subject' => 'Panel Test Message',
|
||||
'body' => 'This is a test of the Panel mail system. You\'re good to go!',
|
||||
],
|
||||
];
|
||||
@@ -1,384 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<Container version="2">
|
||||
<Name>pelican-panel</Name>
|
||||
<Repository>ghcr.io/pelican-dev/panel:latest</Repository>
|
||||
<Registry>https://github.com/pelican-dev/panel/pkgs/container/panel</Registry>
|
||||
<Branch>
|
||||
<Tag>latest</Tag>
|
||||
<TagDescription>Latest stable release</TagDescription>
|
||||
</Branch>
|
||||
<Branch>
|
||||
<Tag>develop</Tag>
|
||||
<TagDescription>Development build (unstable)</TagDescription>
|
||||
</Branch>
|
||||
<Network>bridge</Network>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>false</Privileged>
|
||||
<Support>https://discord.gg/pelican-panel</Support>
|
||||
<Project>https://pelican.dev</Project>
|
||||
<Overview>Pelican Panel is an open-source, free game server management panel built with modern PHP and React. It supports Minecraft, Rust, Terraria, CS2, and many more out of the box. Features include multi-user access, server scheduling, file management, and a powerful API.</Overview>
|
||||
<Category>GameServers: Tools:</Category>
|
||||
<WebUI>http://[IP]:[PORT:80]</WebUI>
|
||||
<Icon>https://raw.githubusercontent.com/pelican-dev/panel/main/public/pelican.svg</Icon>
|
||||
<Requires>Ensure port 80 and 443 are not in use by another container or Unraid's built-in web server. If using HTTPS (APP_URL starts with https://), the WebUI link below will still show HTTP — navigate manually using your HTTPS URL.</Requires>
|
||||
<ExtraParams>--restart=unless-stopped</ExtraParams>
|
||||
|
||||
<!-- Ports -->
|
||||
<Config
|
||||
Name="HTTP Port"
|
||||
Target="80"
|
||||
Default="80"
|
||||
Mode="tcp"
|
||||
Description="Web interface HTTP port."
|
||||
Type="Port"
|
||||
Display="always"
|
||||
Required="true"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="HTTPS Port"
|
||||
Target="443"
|
||||
Default="443"
|
||||
Mode="tcp"
|
||||
Description="Web interface HTTPS port. Used when APP_URL starts with https:// and LE_EMAIL is set."
|
||||
Type="Port"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Volumes -->
|
||||
<Config
|
||||
Name="Panel Data"
|
||||
Target="/pelican-data"
|
||||
Default="/mnt/user/appdata/pelican-panel/data"
|
||||
Mode="rw"
|
||||
Description="Persistent storage for the panel (database, .env, uploads, plugins)."
|
||||
Type="Path"
|
||||
Display="always"
|
||||
Required="true"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Panel Logs"
|
||||
Target="/var/www/html/storage/logs"
|
||||
Default="/mnt/user/appdata/pelican-panel/logs"
|
||||
Mode="rw"
|
||||
Description="Application log files."
|
||||
Type="Path"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Core Application Settings -->
|
||||
<Config
|
||||
Name="App URL"
|
||||
Target="APP_URL"
|
||||
Default="http://[IP]"
|
||||
Mode=""
|
||||
Description="The full URL used to access the panel (e.g. http://192.168.1.50 or https://panel.example.com). Must match how you access it in your browser."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="true"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="App Environment"
|
||||
Target="APP_ENV"
|
||||
Default="production"
|
||||
Mode=""
|
||||
Description="Application environment. Leave as production."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Debug Mode"
|
||||
Target="APP_DEBUG"
|
||||
Default="false"
|
||||
Mode=""
|
||||
Description="Enable debug mode. Only enable for troubleshooting."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Database Configuration -->
|
||||
<Config
|
||||
Name="Database Driver"
|
||||
Target="DB_CONNECTION"
|
||||
Default="sqlite|mysql|mariadb|pgsql"
|
||||
Mode=""
|
||||
Description="Database engine to use. SQLite requires no extra setup and stores data in the Panel Data volume. MySQL/MariaDB/PostgreSQL require a separate database server."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="true"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Database Host"
|
||||
Target="DB_HOST"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="Database server hostname or IP. Not needed for SQLite. Use the Unraid IP or container name if running a database container."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Database Port"
|
||||
Target="DB_PORT"
|
||||
Default="3306"
|
||||
Mode=""
|
||||
Description="Database server port. Default: 3306 for MySQL/MariaDB, 5432 for PostgreSQL. Not needed for SQLite."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Database Name"
|
||||
Target="DB_DATABASE"
|
||||
Default="panel"
|
||||
Mode=""
|
||||
Description="Name of the database. Not needed for SQLite."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Database Username"
|
||||
Target="DB_USERNAME"
|
||||
Default="pelican"
|
||||
Mode=""
|
||||
Description="Database user. Not needed for SQLite."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Database Password"
|
||||
Target="DB_PASSWORD"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="Database password. Not needed for SQLite."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="true"
|
||||
/>
|
||||
|
||||
<!-- SSL / Let's Encrypt -->
|
||||
<Config
|
||||
Name="Let's Encrypt Email"
|
||||
Target="LE_EMAIL"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="Email for automatic Let's Encrypt SSL certificates. Only used when APP_URL starts with https://. Leave empty to use HTTP only."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Reverse Proxy Settings -->
|
||||
<Config
|
||||
Name="Behind Proxy"
|
||||
Target="BEHIND_PROXY"
|
||||
Default="false|true"
|
||||
Mode=""
|
||||
Description="Set to true if this container is behind a reverse proxy (e.g. Nginx Proxy Manager, Swag, Caddy)."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Trusted Proxies"
|
||||
Target="TRUSTED_PROXIES"
|
||||
Default="172.17.0.0/12"
|
||||
Mode=""
|
||||
Description="Comma-separated list of trusted proxy IPs or CIDR ranges. Only used when Behind Proxy is true."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Skip Built-in Caddy"
|
||||
Target="SKIP_CADDY"
|
||||
Default="false|true"
|
||||
Mode=""
|
||||
Description="Set to true to disable the built-in Caddy web server. Use this when your reverse proxy connects directly to PHP-FPM on port 9000."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Mail Configuration -->
|
||||
<Config
|
||||
Name="Mail Driver"
|
||||
Target="MAIL_MAILER"
|
||||
Default="log|smtp|sendmail|mailgun|postmark"
|
||||
Mode=""
|
||||
Description="Mail transport method. 'log' writes emails to the log file (no actual sending). Use 'smtp' to send real emails."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="SMTP Host"
|
||||
Target="MAIL_HOST"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="SMTP server hostname (e.g. smtp.gmail.com, smtp.mailgun.org)."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="SMTP Port"
|
||||
Target="MAIL_PORT"
|
||||
Default="587"
|
||||
Mode=""
|
||||
Description="SMTP server port. Common values: 587 (TLS), 465 (SSL), 25 (unencrypted)."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="SMTP Username"
|
||||
Target="MAIL_USERNAME"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="SMTP authentication username."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="SMTP Password"
|
||||
Target="MAIL_PASSWORD"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="SMTP authentication password."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="true"
|
||||
/>
|
||||
<Config
|
||||
Name="Mail Encryption"
|
||||
Target="MAIL_SCHEME"
|
||||
Default="|tls|ssl"
|
||||
Mode=""
|
||||
Description="SMTP encryption scheme. Leave empty for no encryption."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Mail From Address"
|
||||
Target="MAIL_FROM_ADDRESS"
|
||||
Default="no-reply@example.com"
|
||||
Mode=""
|
||||
Description="The sender email address for outgoing mail."
|
||||
Type="Variable"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Mail From Name"
|
||||
Target="MAIL_FROM_NAME"
|
||||
Default="Pelican Panel"
|
||||
Mode=""
|
||||
Description="The sender name shown in outgoing emails."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Cache / Session / Queue -->
|
||||
<Config
|
||||
Name="Cache Driver"
|
||||
Target="CACHE_STORE"
|
||||
Default="file|redis"
|
||||
Mode=""
|
||||
Description="Cache backend. 'file' works out of the box. Use 'redis' for better performance with multiple users."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Session Driver"
|
||||
Target="SESSION_DRIVER"
|
||||
Default="file|redis"
|
||||
Mode=""
|
||||
Description="Session storage backend."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Queue Driver"
|
||||
Target="QUEUE_CONNECTION"
|
||||
Default="sync|database|redis"
|
||||
Mode=""
|
||||
Description="Queue processing backend. 'sync' processes jobs immediately. 'database' or 'redis' process in the background."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
|
||||
<!-- Redis (Advanced) -->
|
||||
<Config
|
||||
Name="Redis Host"
|
||||
Target="REDIS_HOST"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="Redis server hostname or IP. Only needed if using redis for cache, session, or queue. Use the Unraid IP or Redis container name (e.g. redis)."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Redis Port"
|
||||
Target="REDIS_PORT"
|
||||
Default="6379"
|
||||
Mode=""
|
||||
Description="Redis server port."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false"
|
||||
/>
|
||||
<Config
|
||||
Name="Redis Password"
|
||||
Target="REDIS_PASSWORD"
|
||||
Default=""
|
||||
Mode=""
|
||||
Description="Redis server password. Leave empty if no authentication is required."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="true"
|
||||
/>
|
||||
</Container>
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Handle X-Forwarded-Proto Header
|
||||
RewriteCond %{HTTP:X-Forwarded-Proto} =https [NC]
|
||||
RewriteRule .* - [E=HTTPS:on]
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
@@ -9,8 +9,10 @@ use App\Models\Backup;
|
||||
use App\Repositories\Daemon\DaemonBackupRepository;
|
||||
use App\Services\Backups\DeleteBackupService;
|
||||
use App\Tests\Integration\IntegrationTestCase;
|
||||
use Aws\CommandInterface;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Mockery;
|
||||
|
||||
class DeleteBackupServiceTest extends IntegrationTestCase
|
||||
{
|
||||
@@ -92,10 +94,12 @@ class DeleteBackupServiceTest extends IntegrationTestCase
|
||||
$manager->expects('adapter')->with(Backup::ADAPTER_AWS_S3)->andReturn($adapter);
|
||||
|
||||
$adapter->expects('getBucket')->andReturn('foobar');
|
||||
$adapter->expects('getClient->deleteObject')->with([
|
||||
$mockCommand = Mockery::mock(CommandInterface::class);
|
||||
$adapter->expects('getClient->getCommand')->with('DeleteObject', [
|
||||
'Bucket' => 'foobar',
|
||||
'Key' => sprintf('%s/%s.tar.gz', $server->uuid, $backup->uuid),
|
||||
]);
|
||||
])->andReturn($mockCommand);
|
||||
$adapter->expects('executeS3Command')->with($mockCommand);
|
||||
|
||||
$this->app->make(DeleteBackupService::class)->handle($backup);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user