Initial commit

This commit is contained in:
NekoMonci12
2025-04-26 11:18:20 +07:00
commit 1d1607e307
5 changed files with 286 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

138
admin/controller.php Normal file
View File

@@ -0,0 +1,138 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Extensions\s3manager;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\View\Factory as ViewFactory;
use Illuminate\Support\Facades\File;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\BlueprintFramework\Libraries\ExtensionLibrary\Admin\BlueprintAdminLibrary as BlueprintExtensionLibrary;
class s3managerExtensionController extends Controller
{
public function __construct(
private ViewFactory $view,
private BlueprintExtensionLibrary $blueprint,
) {}
/**
* Handle both GET and POST requests.
*/
public function index(Request $request): View
{
$message = null;
// Handle backup driver selection
if ($request->has('save_driver')) {
\Artisan::call('config:clear');
$driver = $request->input('APP_BACKUP_DRIVER');
if (!in_array($driver, ['s3', 'wings'])) {
// Use the 'message' variable to pass a message to the view
$message = 'Invalid backup driver selected.';
return $this->view->make(
'admin.extensions.{identifier}.index', [
'root' => "/admin/extensions/{identifier}",
'blueprint' => $this->blueprint,
'message' => $message, // Pass the message to the view
]
);
}
$this->setEnvironmentValue([
'APP_BACKUP_DRIVER' => $driver,
]);
$message = 'Backup driver updated successfully!';
}
// Only validate and save S3-related values if `save_env` was used
if ($request->has('save_env')) {
\Artisan::call('config:clear');
$endpoint = rtrim($request->input('AWS_ENDPOINT'), '/');
$validatedUrl = filter_var($endpoint, FILTER_VALIDATE_URL);
if (!$validatedUrl) {
$message = 'Invalid endpoint URL.';
return $this->view->make(
'admin.extensions.{identifier}.index', [
'root' => "/admin/extensions/{identifier}",
'blueprint' => $this->blueprint,
'message' => $message,
]
);
}
$allowedStorageClasses = ['STANDARD', 'STANDARD_IA', 'GLACIER'];
$storageClass = strtoupper($request->input('AWS_BACKUPS_STORAGE_CLASS'));
if (!in_array($storageClass, $allowedStorageClasses)) {
$message = 'Invalid storage class selected.';
return $this->view->make(
'admin.extensions.{identifier}.index', [
'root' => "/admin/extensions/{identifier}",
'blueprint' => $this->blueprint,
'message' => $message,
]
);
}
$maxPartSizeGB = min((int) $request->input('BACKUP_MAX_PART_SIZE_GB'), 5);
$maxPartSizeBytes = $maxPartSizeGB * 1024 * 1024 * 1024;
$this->setEnvironmentValue([
'AWS_DEFAULT_REGION' => $request->input('AWS_DEFAULT_REGION'),
'AWS_ACCESS_KEY_ID' => $request->input('AWS_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => $request->input('AWS_SECRET_ACCESS_KEY'),
'AWS_BACKUPS_BUCKET' => $request->input('AWS_BACKUPS_BUCKET'),
'AWS_ENDPOINT' => $validatedUrl,
'AWS_USE_PATH_STYLE_ENDPOINT' => $request->input('AWS_USE_PATH_STYLE_ENDPOINT') === 'true' ? 'true' : 'false',
'AWS_BACKUPS_STORAGE_CLASS' => $storageClass,
'BACKUP_MAX_PART_SIZE' => $maxPartSizeBytes,
'BACKUP_PRESIGNED_URL_LIFESPAN' => (int) $request->input('BACKUP_PRESIGNED_URL_LIFESPAN'),
]);
$message = 'Environment variables updated successfully!';
}
// Return the view with the message
return $this->view->make(
'admin.extensions.{identifier}.index', [
'root' => "/admin/extensions/{identifier}",
'blueprint' => $this->blueprint,
'message' => $message, // Pass the message to the view
]
);
}
/**
* Handle POST requests by delegating to index().
*/
public function post(Request $request): View
{
return $this->index($request);
}
protected function setEnvironmentValue(array $values)
{
$envPath = base_path('.env');
$envContent = File::get($envPath);
foreach ($values as $key => $value) {
// If the key already exists, replace it with the new value.
$pattern = "/^$key=.*$/m";
$replacement = $key . '=' . $value;
if (preg_match($pattern, $envContent)) {
// If the key exists, replace the line.
$envContent = preg_replace($pattern, $replacement, $envContent);
} else {
// If the key doesn't exist, append it to the end of the file.
$envContent .= "\n$replacement";
}
}
// Write the updated content back to the .env file.
File::put($envPath, $envContent);
}
}

109
admin/view.blade.php Normal file
View File

@@ -0,0 +1,109 @@
<!--
Content on this page will be displayed on your extension's admin page.
-->
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Information</h3>
</div>
<div class="box-body">
<p>
An Extension For Managing <code>S3 Storage</code> Credentials.
</p>
</div>
</div>
<div class="box box-warning">
<div class="box-header with-border">
<h3 class="box-title">Backup Driver</h3>
</div>
<div class="box-body">
<form method="POST" action="{{ $root }}">
@csrf
<div class="form-group">
<label for="backup_driver">Select Backup Driver</label>
<select name="APP_BACKUP_DRIVER" id="backup_driver" class="form-control">
<option value="s3" {{ env('APP_BACKUP_DRIVER') === 's3' ? 'selected' : '' }}>S3 (Remote)</option>
<option value="wings" {{ env('APP_BACKUP_DRIVER') === 'wings' ? 'selected' : '' }}>Wings (Local)</option>
</select>
</div>
<div class="form-group">
<button type="submit" name="save_driver" value="1" class="btn btn-warning">Save Driver</button>
</div>
</form>
</div>
</div>
<div class="box box-info">
<div class="box-header with-border">
<h3 class="box-title">Management</h3>
</div>
<div class="box-body">
<!-- Display success or error message if available -->
@if(isset($message) && $message)
<div class="alert alert-success">
{{ $message }}
</div>
@endif
<form method="POST" action="{{ $root }}">
@csrf
<div class="form-group">
<label for="region">AWS Region</label>
<input type="text" id="region" name="AWS_DEFAULT_REGION" class="form-control" value="{{ env('AWS_DEFAULT_REGION') }}">
</div>
<div class="form-group">
<label for="key">Access Key ID</label>
<input type="text" id="key" name="AWS_ACCESS_KEY_ID" class="form-control" value="{{ env('AWS_ACCESS_KEY_ID') }}">
</div>
<div class="form-group">
<label for="secret">Secret Access Key</label>
<input type="text" id="secret" name="AWS_SECRET_ACCESS_KEY" class="form-control" value="{{ env('AWS_SECRET_ACCESS_KEY') }}">
</div>
<div class="form-group">
<label for="bucket">Bucket Name</label>
<input type="text" id="bucket" name="AWS_BACKUPS_BUCKET" class="form-control" value="{{ env('AWS_BACKUPS_BUCKET') }}">
</div>
<div class="form-group">
<label for="endpoint">Endpoint</label>
<input type="url" id="endpoint" name="AWS_ENDPOINT" class="form-control" value="{{ rtrim(env('AWS_ENDPOINT'), '/') }}" placeholder="https://s3.example.com">
</div>
<div class="form-group">
<label for="path_style">Path Style Endpoint</label>
<select name="AWS_USE_PATH_STYLE_ENDPOINT" class="form-control">
<option value="true" {{ env('AWS_USE_PATH_STYLE_ENDPOINT') == 'true' ? 'selected' : '' }}>true</option>
<option value="false" {{ env('AWS_USE_PATH_STYLE_ENDPOINT') == 'false' ? 'selected' : '' }}>false</option>
</select>
</div>
<div class="form-group">
<label for="storage_class">Storage Class</label>
<select name="AWS_BACKUPS_STORAGE_CLASS" class="form-control">
<option value="STANDARD" {{ env('AWS_BACKUPS_STORAGE_CLASS') == 'STANDARD' ? 'selected' : '' }}>STANDARD</option>
<option value="STANDARD_IA" {{ env('AWS_BACKUPS_STORAGE_CLASS') == 'STANDARD_IA' ? 'selected' : '' }}>STANDARD_IA</option>
<option value="GLACIER" {{ env('AWS_BACKUPS_STORAGE_CLASS') == 'GLACIER' ? 'selected' : '' }}>GLACIER</option>
</select>
</div>
<div class="form-group">
<label for="part_size">Max Part Size (GB)</label>
<input type="number" min="1" max="5" step="1" id="part_size" name="BACKUP_MAX_PART_SIZE_GB" class="form-control" value="{{ intval(env('BACKUP_MAX_PART_SIZE')) / 1073741824 }}">
</div>
<div class="form-group">
<label for="lifespan">Presigned URL Lifespan (seconds)</label>
<input type="number" id="lifespan" name="BACKUP_PRESIGNED_URL_LIFESPAN" class="form-control" value="{{ env('BACKUP_PRESIGNED_URL_LIFESPAN') }}">
</div>
<div class="form-group">
<button type="submit" name="save_env" value="1" class="btn btn-primary">Save Settings</button>
</div>
</form>
</div>
</div>

37
conf.yml Normal file
View File

@@ -0,0 +1,37 @@
info:
name: "S3 Manager"
identifier: "s3manager"
description: "Setup S3 Bucket For Backups System"
flags: ""
version: "1.0.0"
target: "beta-2024-12"
author: "nekomonci12"
icon: "public/S3Manager.png"
website: ""
admin:
view: "admin/view.blade.php"
controller: "admin/controller.php"
css: ""
wrapper: ""
dashboard:
css: ""
wrapper: ""
components: ""
data:
directory: ""
public: ""
console: ""
requests:
views: ""
app: ""
routers:
application: ""
client: ""
web: ""
database:
migrations: ""

BIN
public/S3Manager.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB