diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 957952b..004bbcd 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -3,6 +3,7 @@ namespace App\Exceptions; use Exception; +use Throwable; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; @@ -23,11 +24,16 @@ class Handler extends ExceptionHandler * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. * - * @param \Exception $e + * @param \Throwable $e * @return void */ - public function report(Exception $e) + public function report(Throwable $e) { + // Laravel 5.0 parent expects an Exception; wrap Error instances so + // PHP 7+ fatal errors (TypeError, ParseError, etc.) are handled safely. + if (!$e instanceof Exception) { + $e = new \RuntimeException($e->getMessage(), $e->getCode()); + } return parent::report($e); } @@ -43,11 +49,13 @@ class Handler extends ExceptionHandler // return parent::render($request, $e); // } - public function render($request, Exception $e) + public function render($request, Throwable $e) { if ($e instanceof MethodNotAllowedHttpException) { abort(404); } - return parent::render($request, $e); + // Wrap non-Exception Throwables for Laravel 5.0 parent compatibility. + $exception = $e instanceof Exception ? $e : new \RuntimeException($e->getMessage(), $e->getCode()); + return parent::render($request, $exception); } } diff --git a/app/Http/Controllers/user/UserController.php b/app/Http/Controllers/user/UserController.php index c18fa7e..1a2deec 100755 --- a/app/Http/Controllers/user/UserController.php +++ b/app/Http/Controllers/user/UserController.php @@ -765,8 +765,7 @@ class UserController extends Controller $u = $UserModel->insertNewProductThumbnails($thumbs); // var_dump($thumbs); - Storage::disk('sftp')->put($thumbnail, fopen($request->file('imgupload')[$i], 'r+')); //live - //Storage::disk('localdir')->put($thumbnail, fopen($request->file('imgupload')[$i], 'r+')); + Storage::disk('minio_crewsportswear')->put('images/' . $thumbnail, fopen($request->file('imgupload')[$i], 'r+')); // var_dump($s); } @@ -938,8 +937,7 @@ class UserController extends Controller ); $u = $UserModel->insertNewProductThumbnails($thumbs); - Storage::disk('sftp')->put($thumbnail, fopen($request->file('upload_images')[$i], 'r+')); //live - //Storage::disk('localdir')->put($thumbnail, fopen($request->file('upload_images')[$i], 'r+')); + Storage::disk('minio_crewsportswear')->put('images/' . $thumbnail, fopen($request->file('upload_images')[$i], 'r+')); } @@ -961,6 +959,8 @@ class UserController extends Controller unlink($storagePath . $file); } + Storage::disk('minio_crewsportswear')->delete('images/' . $file); + $i = $UserModel->deleteImageThumb('Id', $id); return response()->json(array( diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 9e53a91..1912b9f 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,8 @@ use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Storage; use League\Flysystem\Filesystem; use League\Flysystem\Sftp\SftpAdapter; +use League\Flysystem\AwsS3v3\AwsS3Adapter as AwsS3v3Adapter; +use Aws\S3\S3Client; use Illuminate\Support\Facades\Blade; class AppServiceProvider extends ServiceProvider { @@ -27,6 +29,21 @@ class AppServiceProvider extends ServiceProvider { Storage::extend('sftp', function ($app, $config) { return new Filesystem(new SftpAdapter($config)); }); + + Storage::extend('minio', function ($app, $config) { + $client = new S3Client([ + 'credentials' => [ + 'key' => $config['key'], + 'secret' => $config['secret'], + ], + 'region' => $config['region'], + 'version' => 'latest', + 'endpoint' => $config['endpoint'], + 'use_path_style_endpoint' => filter_var($config['use_path_style_endpoint'] ?? true, FILTER_VALIDATE_BOOLEAN), + ]); + $adapter = new AwsS3v3Adapter($client, $config['bucket']); + return new Filesystem($adapter); + }); } /** diff --git a/app/helpers.php b/app/helpers.php new file mode 100644 index 0000000..2fa12d7 --- /dev/null +++ b/app/helpers.php @@ -0,0 +1,33 @@ + 'local', 'root' => 'C:/wamp64/www/uploads', ], + + 'minio' => [ + 'driver' => 'minio', + 'key' => env('MINIO_KEY'), + 'secret' => env('MINIO_SECRET'), + 'region' => env('MINIO_REGION', 'us-east-1'), + 'bucket' => env('MINIO_BUCKET', 'merchbay'), + 'endpoint' => env('MINIO_ENDPOINT'), + 'use_path_style_endpoint' => env('MINIO_USE_PATH_STYLE', true), + 'url' => env('MINIO_URL', 'https://minio.crewsportswear.app'), + ], + + 'minio_crewsportswear' => [ + 'driver' => 'minio', + 'key' => env('MINIO_KEY'), + 'secret' => env('MINIO_SECRET'), + 'region' => env('MINIO_REGION', 'us-east-1'), + 'bucket' => 'crewsportswear', + 'endpoint' => env('MINIO_ENDPOINT'), + 'use_path_style_endpoint' => env('MINIO_USE_PATH_STYLE', true), + 'url' => env('MINIO_URL', 'https://minio.crewsportswear.app'), + ], ], ]; diff --git a/config/site_config.php b/config/site_config.php index 34527b8..b01ddc2 100755 --- a/config/site_config.php +++ b/config/site_config.php @@ -20,6 +20,7 @@ return [ 'prod_private_server_ip' => env('PROD_PRIVATE'), // 'images_url' => env('https://crewsportswear.app:5955', 'https://crewsportswear.app:5955'), 'images_url' => env('IMAGES_URL'), + 'minio_url' => env('MINIO_URL', 'https://minio.crewsportswear.app/merchbay'), // 'uploads' => env('https://crewsportswear.app:5955/merchbay/', 'https://crewsportswear.com/uploads/images/'), // local 'uploads' => env('UPLOAD_URL'), diff --git a/docker-compose.local.yml b/docker-compose.local.yml index f49a4be..5a14346 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -1,3 +1,19 @@ +# ───────────────────────────────────────────────────────────────────────────── +# Local development stack +# +# Default (local MariaDB): +# docker compose --env-file .env.local -f docker-compose.local.yml up --build +# +# Remote DB via SSH private-key tunnel: +# 1. Set SSH_HOST, SSH_USER, SSH_KEY_PATH, SSH_DB_REMOTE_HOST, +# SSH_DB_REMOTE_PORT (and DB_* creds) in .env.local +# 2. docker compose --env-file .env.local -f docker-compose.local.yml --profile ssh-db up --build +# The app will talk to db-tunnel (port 3306) instead of the local db. +# +# App: http://localhost:8080 +# phpMyAdmin: http://localhost:8081 +# ───────────────────────────────────────────────────────────────────────────── + services: db: image: mariadb:10.6 @@ -5,10 +21,10 @@ services: container_name: merchbay_db_local restart: unless-stopped environment: - MYSQL_DATABASE: merchbay + MYSQL_DATABASE: ${DB_DATABASE:-merchbay_db} MYSQL_ROOT_PASSWORD: root - MYSQL_USER: merchbay - MYSQL_PASSWORD: secret + MYSQL_USER: ${DB_USERNAME:-merchbay} + MYSQL_PASSWORD: ${DB_PASSWORD:-secret} ports: - "3306:3306" volumes: @@ -29,14 +45,14 @@ services: - APP_DEBUG=true - APP_URL=http://localhost:8080 - DB_CONNECTION=mysql - - DB_HOST=db - - DB_PORT=3306 - - DB_DATABASE=merchbay - - DB_USERNAME=merchbay - - DB_PASSWORD=secret + - DB_HOST=${DB_HOST:-db} + - DB_PORT=${DB_PORT:-3306} + - DB_DATABASE=${DB_DATABASE:-merchbay_db} + - DB_USERNAME=${DB_USERNAME:-merchbay} + - DB_PASSWORD=${DB_PASSWORD:-secret} - PROD_PRIVATE=http://localhost:8080 - - IMAGES_URL=http://localhost:8080 - - UPLOAD_URL=http://localhost:8080/uploads/ + - IMAGES_URL=${IMAGES_URL:-https://minio.crewsportswear.app/merchbay} + - UPLOAD_URL=${UPLOAD_URL:-https://minio.crewsportswear.app/merchbay/uploads/} - MAIL_DRIVER=log - MAIL_HOST=localhost - MAIL_PORT=1025 @@ -48,15 +64,67 @@ services: - ANALYTICS_SITE_ID= - ANALYTICS_CLIENT_ID= - ANALYTICS_SERVICE_EMAIL= + # MinIO S3 Storage (connect to production MinIO for testing) + - MINIO_ENDPOINT=https://minio.crewsportswear.app + - MINIO_KEY=${MINIO_KEY:-minioadmin} + - MINIO_SECRET=${MINIO_SECRET:-minioadmin} + - MINIO_BUCKET=merchbay + - MINIO_REGION=us-east-1 + - MINIO_USE_PATH_STYLE=false + - MINIO_URL=https://minio.crewsportswear.app + env_file: + - path: .env.local + required: false volumes: - ./:/var/www/html - ./storage:/var/www/html/storage - ./public/uploads:/var/www/html/public/uploads + # Keep the vendor/ directory from the image — prevents the bind-mount + # from wiping out the composer install done during docker build. + - vendor_local:/var/www/html/vendor depends_on: - db networks: - merchbay-local + # ── SSH tunnel to a remote database ──────────────────────────────────────── + # Activated only with: --profile ssh-db + # Requires SSH_HOST, SSH_USER, SSH_KEY_PATH (and optionally SSH_PORT, + # SSH_DB_REMOTE_HOST, SSH_DB_REMOTE_PORT) set in .env.local. + db-tunnel: + profiles: + - ssh-db + image: alpine:3.19 + container_name: merchbay_db_tunnel + restart: unless-stopped + environment: + - SSH_HOST=${SSH_HOST} + - SSH_PORT=${SSH_PORT:-22} + - SSH_USER=${SSH_USER:-root} + - SSH_DB_REMOTE_HOST=${SSH_DB_REMOTE_HOST:-127.0.0.1} + - SSH_DB_REMOTE_PORT=${SSH_DB_REMOTE_PORT:-3306} + volumes: + - ${SSH_KEY_PATH:-~/.ssh/id_rsa}:/ssh/id_rsa:ro + command: + - sh + - -c + - | + apk add --no-cache openssh-client + cp /ssh/id_rsa /tmp/id_rsa + chmod 600 /tmp/id_rsa + echo "Starting SSH tunnel to $$SSH_HOST..." + exec ssh -N \ + -o StrictHostKeyChecking=no \ + -o ServerAliveInterval=30 \ + -o ServerAliveCountMax=3 \ + -o ExitOnForwardFailure=yes \ + -i /tmp/id_rsa \ + -L "0.0.0.0:3306:$$SSH_DB_REMOTE_HOST:$$SSH_DB_REMOTE_PORT" \ + -p "$$SSH_PORT" \ + "$$SSH_USER@$$SSH_HOST" + networks: + - merchbay-local + phpmyadmin: image: arm64v8/phpmyadmin platform: linux/arm64 @@ -79,3 +147,4 @@ networks: volumes: db_data: + vendor_local: diff --git a/resources/views/designer/designer.blade.php b/resources/views/designer/designer.blade.php index 349a62a..77da716 100755 --- a/resources/views/designer/designer.blade.php +++ b/resources/views/designer/designer.blade.php @@ -450,7 +450,7 @@ @foreach ($pattern_arrays as $i => $val)
');
+ $('#teamname_text_shape').html('Text Shape:
');
}else if(obj.effect == "arc"){
- $('#teamname_text_shape').html('Text Shape:
');
+ $('#teamname_text_shape').html('Text Shape:
');
}else{
$('#teamname_text_shape').html('Add text Shape');
}
@@ -3395,7 +3395,7 @@
function loadSVGClipart(dataUrl){
var k = 0;
var arrayPathId = [];
- var svgUrl = "{{ config('site_config.uploads') }}cliparts/" + dataUrl;
+ var svgUrl = "{{ minio_url('cliparts/') }}" + dataUrl;
fabric.loadSVGFromURL(svgUrl, function(objects, options) {
var clipart = fabric.util.groupSVGElements(objects, options );
clipart.set({
diff --git a/resources/views/emails/orders.blade.php b/resources/views/emails/orders.blade.php
index 25c0d88..c8f58b2 100755
--- a/resources/views/emails/orders.blade.php
+++ b/resources/views/emails/orders.blade.php
@@ -455,7 +455,7 @@
@foreach ($img_thumb as $img)
@if ($img->ProductId == $item->ProductId)
+ src="{{ config('filesystems.disks.minio.url') }}/crewsportswear/images/{{ $img->Image }}">
@endif
@endforeach
diff --git a/resources/views/merchbay/cart.blade.php b/resources/views/merchbay/cart.blade.php
index e901282..aa384a5 100755
--- a/resources/views/merchbay/cart.blade.php
+++ b/resources/views/merchbay/cart.blade.php
@@ -63,7 +63,7 @@