Skip to content

Commit

Permalink
Added S3 Storage Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
takielias committed Aug 26, 2024
1 parent c4ff1de commit 4ab9353
Show file tree
Hide file tree
Showing 18 changed files with 585 additions and 65 deletions.
2 changes: 2 additions & 0 deletions app/Enums/StorageProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ final class StorageProvider
const FTP = 'ftp';

const LOCAL = 'local';
const S3 = 's3';

const WASABI = 'wasabi';
}
20 changes: 20 additions & 0 deletions app/SSH/HasS3Storage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\SSH;

trait HasS3Storage
{
private function prepareS3Path(string $path, string $prefix = ''): string
{
$path = trim($path);
$path = ltrim($path, '/');
$path = preg_replace('/[^a-zA-Z0-9\-_\.\/]/', '_', $path);
$path = preg_replace('/\/+/', '/', $path);

if ($prefix) {
$path = trim($prefix, '/') . '/' . $path;
}

return $path;
}
}
82 changes: 82 additions & 0 deletions app/SSH/Storage/S3.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace App\SSH\Storage;

use App\Exceptions\SSHCommandError;
use App\Models\Server;
use App\Models\StorageProvider;
use App\SSH\HasS3Storage;
use App\SSH\HasScripts;
use Illuminate\Support\Facades\Log;

class S3 extends S3AbstractStorage
{
use HasScripts, HasS3Storage;

public function __construct(Server $server, StorageProvider $storageProvider)
{
parent::__construct($server, $storageProvider);
$this->setBucketRegion($this->storageProvider->credentials['region']);
$this->setApiUrl();
}

/**
* @throws SSHCommandError
*/
public function upload(string $src, string $dest): array
{
$uploadCommand = $this->getScript('s3/upload.sh', [
'src' => $src,
'bucket' => $this->storageProvider->credentials['bucket'],
'dest' => $this->prepareS3Path($this->storageProvider->credentials['path'] . '/' . $dest),
'key' => $this->storageProvider->credentials['key'],
'secret' => $this->storageProvider->credentials['secret'],
'region' => $this->getBucketRegion(),
'endpoint' => $this->getApiUrl()
]);

$upload = $this->server->ssh()->exec($uploadCommand, 'upload-to-s3');

if (str_contains($upload, 'Error') || !str_contains($upload, 'upload:')) {
Log::error('Failed to upload to S3', ['output' => $upload]);
throw new SSHCommandError('Failed to upload to S3: ' . $upload);
}

return [
'size' => null, // You can parse the size from the output if needed
];

}

/**
* @throws SSHCommandError
*/
public function download(string $src, string $dest): void
{
$downloadCommand = $this->getScript('s3/download.sh', [
'src' => $this->prepareS3Path($this->storageProvider->credentials['path'] . '/' . $src),
'dest' => $dest,
'bucket' => $this->storageProvider->credentials['bucket'],
'key' => $this->storageProvider->credentials['key'],
'secret' => $this->storageProvider->credentials['secret'],
'region' => $this->getBucketRegion(),
'endpoint' => $this->getApiUrl()
]);

$download = $this->server->ssh()->exec($downloadCommand, 'download-from-s3');

if (!str_contains($download, 'Download successful')) {
Log::error('Failed to download from S3', ['output' => $download]);
throw new SSHCommandError('Failed to download from S3: ' . $download);
}
}

/**
* @TODO Implement delete method
*/
public function delete(string $path): void
{

}

}
31 changes: 31 additions & 0 deletions app/SSH/Storage/S3AbstractStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace App\SSH\Storage;

abstract class S3AbstractStorage extends AbstractStorage
{
protected ?string $apiUrl = null;
protected ?string $bucketRegion = null;

public function getApiUrl(): string
{
return $this->apiUrl;
}

public function setApiUrl(string $region = null): void
{
$this->bucketRegion = $region ?? $this->bucketRegion;
$this->apiUrl = "https://s3.{$this->bucketRegion}.amazonaws.com";
}

// Getter and Setter for $bucketRegion
public function getBucketRegion(): string
{
return $this->bucketRegion;
}

public function setBucketRegion(string $region): void
{
$this->bucketRegion = $region;
}
}
43 changes: 31 additions & 12 deletions app/SSH/Storage/Wasabi.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@
namespace App\SSH\Storage;

use App\Exceptions\SSHCommandError;
use App\Models\Server;
use App\Models\StorageProvider;
use App\SSH\HasS3Storage;
use App\SSH\HasScripts;
use Illuminate\Support\Facades\Log;

class Wasabi extends AbstractStorage
class Wasabi extends S3AbstractStorage
{
use HasScripts;
use HasScripts, HasS3Storage;

public function __construct(Server $server, StorageProvider $storageProvider)
{
parent::__construct($server, $storageProvider);
$this->setBucketRegion($this->storageProvider->credentials['region']);
$this->setApiUrl();
}

/**
* @throws SSHCommandError
Expand All @@ -18,17 +28,18 @@ public function upload(string $src, string $dest): array
$uploadCommand = $this->getScript('wasabi/upload.sh', [
'src' => $src,
'bucket' => $this->storageProvider->credentials['bucket'],
'dest' => $dest,
'dest' => $this->prepareS3Path($this->storageProvider->credentials['path'] . '/' . $dest),
'key' => $this->storageProvider->credentials['key'],
'secret' => $this->storageProvider->credentials['secret'],
'region' => $this->storageProvider->credentials['region']
'region' => $this->storageProvider->credentials['region'],
'endpoint' => $this->getApiUrl()
]);

$upload = $this->server->ssh()->exec($uploadCommand, 'upload-to-s3');
$upload = $this->server->ssh()->exec($uploadCommand, 'upload-to-wasabi');

if (str_contains($upload, 'Error') || !str_contains($upload, 'upload:')) {
Log::error('Failed to upload to S3', ['output' => $upload]);
throw new SSHCommandError('Failed to upload to S3: ' . $upload);
Log::error('Failed to upload to wasabi', ['output' => $upload]);
throw new SSHCommandError('Failed to upload to wasabi: ' . $upload);
}

return [
Expand All @@ -43,19 +54,20 @@ public function upload(string $src, string $dest): array
public function download(string $src, string $dest): void
{
$downloadCommand = $this->getScript('wasabi/download.sh', [
'src' => $src,
'src' => $this->prepareS3Path($this->storageProvider->credentials['path'] . '/' . $src),
'dest' => $dest,
'bucket' => $this->storageProvider->credentials['bucket'],
'key' => $this->storageProvider->credentials['key'],
'secret' => $this->storageProvider->credentials['secret'],
'region' => $this->storageProvider->credentials['region']
'region' => $this->storageProvider->credentials['region'],
'endpoint' => $this->getApiUrl()
]);

$download = $this->server->ssh()->exec($downloadCommand, 'download-from-s3');
$download = $this->server->ssh()->exec($downloadCommand, 'download-from-wasabi');

if (!str_contains($download, 'Download successful')) {
Log::error('Failed to download from S3', ['output' => $download]);
throw new SSHCommandError('Failed to download from S3: ' . $download);
Log::error('Failed to download from wasabi', ['output' => $download]);
throw new SSHCommandError('Failed to download from wasabi: ' . $download);
}
}

Expand All @@ -66,4 +78,11 @@ public function delete(string $path): void
{

}

public function setApiUrl(string $region = null): void
{
$this->bucketRegion = $region ?? $this->bucketRegion;
$this->apiUrl = "https://{$this->storageProvider->credentials['bucket']}.s3.{$this->getBucketRegion()}.wasabisys.com";
}

}
32 changes: 32 additions & 0 deletions app/SSH/Storage/scripts/s3/download.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash

# Configure AWS CLI with provided credentials
/usr/local/bin/aws configure set aws_access_key_id "__key__"
/usr/local/bin/aws configure set aws_secret_access_key "__secret__"
/usr/local/bin/aws configure set default.region "__region__"

# Use the provided endpoint in the correct format
ENDPOINT="__endpoint__"
BUCKET="__bucket__"
REGION="__region__"

# Ensure that DEST does not have a trailing slash
SRC="__src__"
DEST="__dest__"

# Download the file from S3
echo "Downloading s3://__bucket__/__src__ to __dest__"
download_output=$(/usr/local/bin/aws s3 cp "s3://$BUCKET/$SRC" "$DEST" --endpoint-url="$ENDPOINT" --region "$REGION" 2>&1)
download_exit_code=$?

# Log output and exit code
echo "Download command output: $download_output"
echo "Download command exit code: $download_exit_code"

# Check if the download was successful
if [ $download_exit_code -eq 0 ]; then
echo "Download successful"
else
echo "Download failed"
exit 1
fi
59 changes: 59 additions & 0 deletions app/SSH/Storage/scripts/s3/upload.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/bash

# Check if AWS CLI is installed
if ! command -v aws &> /dev/null
then
echo "AWS CLI is not installed. Installing..."

# Detect system architecture
ARCH=$(uname -m)
if [ "$ARCH" == "x86_64" ]; then
CLI_URL="https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"
elif [ "$ARCH" == "aarch64" ]; then
CLI_URL="https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip"
else
echo "Unsupported architecture: $ARCH"
exit 1
fi

# Download and install AWS CLI
sudo curl "$CLI_URL" -o "awscliv2.zip"
sudo unzip awscliv2.zip
sudo ./aws/install --update
sudo rm -rf awscliv2.zip aws

echo "AWS CLI installation completed."
else
echo "AWS CLI is already installed."
/usr/local/bin/aws --version
fi

# Configure AWS CLI with provided credentials
/usr/local/bin/aws configure set aws_access_key_id "__key__"
/usr/local/bin/aws configure set aws_secret_access_key "__secret__"

# Use the provided endpoint in the correct format
ENDPOINT="__endpoint__"
BUCKET="__bucket__"
REGION="__region__"

# Ensure that DEST does not have a trailing slash
SRC="__src__"
DEST="__dest__"

# Upload the file
echo "Uploading __src__ to s3://$BUCKET/$DEST"
upload_output=$(/usr/local/bin/aws s3 cp "$SRC" "s3://$BUCKET/$DEST" --endpoint-url="$ENDPOINT" --region "$REGION" 2>&1)
upload_exit_code=$?

# Log output and exit code
echo "Upload command output: $upload_output"
echo "Upload command exit code: $upload_exit_code"

# Check if the upload was successful
if [ $upload_exit_code -eq 0 ]; then
echo "Upload successful"
else
echo "Upload failed"
exit 1
fi
11 changes: 8 additions & 3 deletions app/SSH/Storage/scripts/wasabi/download.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
# Configure AWS CLI with provided credentials
/usr/local/bin/aws configure set aws_access_key_id "__key__"
/usr/local/bin/aws configure set aws_secret_access_key "__secret__"
/usr/local/bin/aws configure set default.region "__region__"

# Use the provided endpoint in the correct format
ENDPOINT="https://s3.__region__.wasabisys.com"
ENDPOINT="__endpoint__"
BUCKET="__bucket__"
REGION="__region__"

# Ensure that DEST does not have a trailing slash
SRC="__src__"
DEST="__dest__"

# Download the file from S3
echo "Downloading s3://__bucket____src__ to __dest__"
download_output=$(/usr/local/bin/aws s3 cp "s3://__bucket____src__" "__dest__" --endpoint-url="$ENDPOINT" --region "__region__" 2>&1)
download_output=$(/usr/local/bin/aws s3 cp "s3://$BUCKET/$SRC" "$DEST" --endpoint-url="$ENDPOINT" --region "$REGION" 2>&1)
download_exit_code=$?

# Log output and exit code
Expand Down
14 changes: 8 additions & 6 deletions app/SSH/Storage/scripts/wasabi/upload.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,19 @@ fi
# Configure AWS CLI with provided credentials
/usr/local/bin/aws configure set aws_access_key_id "__key__"
/usr/local/bin/aws configure set aws_secret_access_key "__secret__"
/usr/local/bin/aws configure set default.region "__region__"

# Construct the endpoint URL with the bucket name
ENDPOINT="https://__bucket__.s3.__region__.wasabisys.com"
# Use the provided endpoint in the correct format
ENDPOINT="__endpoint__"
BUCKET="__bucket__"
REGION="__region__"

# Ensure that DEST does not have a trailing slash
DEST="${DEST%/}"
SRC="__src__"
DEST="__dest__"

# Upload the file
echo "Uploading __src__ to s3://__bucket__/$DEST"
upload_output=$(/usr/local/bin/aws s3 cp "__src__" "s3://__bucket__/$DEST" --endpoint-url="$ENDPOINT" --region "__region__" 2>&1)
echo "Uploading __src__ to s3://$BUCKET/$DEST"
upload_output=$(/usr/local/bin/aws s3 cp "$SRC" "s3://$BUCKET/$DEST" --endpoint-url="$ENDPOINT" --region "$REGION" 2>&1)
upload_exit_code=$?

# Log output and exit code
Expand Down
Loading

0 comments on commit 4ab9353

Please sign in to comment.