Skip to content

Commit

Permalink
feat: s3 storage support for images
Browse files Browse the repository at this point in the history
  • Loading branch information
Justintime50 committed Dec 12, 2024
1 parent 28d90d8 commit 3096dc5
Show file tree
Hide file tree
Showing 22 changed files with 668 additions and 185 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Adds PHP 8.4 support
- Docker Swarm support (changes made to configuration and tooling)
- S3 file support for images
- New auth view layouts
- Bumps Docker image with improved PHP-FPM throughput
- Bumps dependencies
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Glass draws its simplistic design inspiration from [Medium](https://medium.com)

## Features

- Custom image support per post
- Custom image support per post (local storage or S3 configurable)
- Comments (can be enabled/disabled)
- Syntax highlighting for code snippets
- Themes to style your blog instance
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
- src/.env
labels:
- 'traefik.enable=true'
- 'traefik.docker.network=traefik'
- 'traefik.http.routers.glass.rule=Host(`glass.localhost`)'
depends_on:
glass-db:
Expand Down
5 changes: 3 additions & 2 deletions src/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ MAIL_FROM_NAME=Glass

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_DEFAULT_REGION=
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_ENDPOINT=
AWS_PUBLIC_URL=

VITE_APP_NAME=Glass

Expand Down
66 changes: 51 additions & 15 deletions src/app/Http/Controllers/ImageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;

class ImageController extends Controller
Expand Down Expand Up @@ -48,19 +49,30 @@ public function uploadPostImage(Request $request): RedirectResponse
try {
$file = $request->file('image');
$filename = self::sanatizeImageFilename($file);

ImageManager::gd()
->read($file)
->save(self::getImagePublicPath(self::$postImagesSubdirectory, $filename));
$subdirectory = self::$postImagesSubdirectory;
$temporaryPath = sys_get_temp_dir() . '/' . $filename;
ImageManager::gd()->read($file)->save($temporaryPath);

if (config('filesystems.default') === 's3') {
$s3Path = self::$imagesDir . "/$subdirectory/$filename";
$success = Storage::disk('s3')->put($s3Path, file_get_contents($temporaryPath));
unlink($temporaryPath);
if (!$success) {
throw new \Exception();
}
} else {
$localPath = self::getImagePublicPath($subdirectory, $filename);
rename($temporaryPath, $localPath);
}

$image = new Image();
$image->subdirectory = self::$postImagesSubdirectory;
$image->subdirectory = $subdirectory;
$image->filename = $filename;
$image->save();

session()->flash('message', 'Image uploaded successfully.');
} catch (\Exception $error) {
session()->flash('error', "Image upload failed: $error");
session()->flash('error', 'Image upload failed, please try again.');
}

return redirect()->back();
Expand All @@ -77,11 +89,27 @@ public function deletePostImage(Request $request, int $id): RedirectResponse
{
try {
$image = Image::findOrFail($id);
unlink(self::getImagePublicPath($image->subdirectory, $image->filename));
} catch (ModelNotFoundException $e) {
session()->flash('error', 'Image not found.');
return redirect()->back();
}

try {
if (config('filesystems.default') === 's3') {
$s3Path = self::$imagesDir . "/$image->subdirectory/$image->filename";
$success = Storage::disk('s3')->delete($s3Path);
if (!$success) {
throw new \Exception();
}
} else {
$localPath = self::getImagePublicPath($image->subdirectory, $image->filename);
unlink($localPath);
}

$image->delete();
session()->flash('message', 'Image deleted.');
} catch (ModelNotFoundException $e) {
// Don't delete an image that doesn't exist
} catch (\Exception $error) {
session()->flash('error', 'Image deletion failed, please try again.');
}

return redirect()->back();
Expand Down Expand Up @@ -111,9 +139,13 @@ public static function sanatizeImageFilename($file): string
*/
public static function getImageAssetPath(?string $subdirectory, ?string $imageName): string|null
{
return isset($subdirectory) && isset($imageName)
? asset('storage/' . self::$imagesDir . "/$subdirectory/$imageName")
: null;
if (config('filesystems.default') === 's3') {
return config('filesystems.disks.s3.public_url') . '/' . self::$imagesDir . "/$subdirectory/$imageName";
} else {
return isset($subdirectory, $imageName)
? asset('storage/' . self::$imagesDir . "/$subdirectory/$imageName")
: null;
}
}

/**
Expand All @@ -125,8 +157,12 @@ public static function getImageAssetPath(?string $subdirectory, ?string $imageNa
*/
public static function getImagePublicPath(?string $subdirectory, ?string $imageName): string|null
{
return isset($subdirectory) && isset($imageName)
? public_path('storage/' . self::$imagesDir . "/$subdirectory/$imageName")
: null;
if (config('filesystems.default') === 's3') {
return config('filesystems.disks.s3.public_url') . '/' . self::$imagesDir . "/$subdirectory/$imageName";
} else {
return isset($subdirectory, $imageName)
? public_path('storage/' . self::$imagesDir . "/$subdirectory/$imageName")
: null;
}
}
}
33 changes: 28 additions & 5 deletions src/app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Intervention\Image\ImageManager;

Expand Down Expand Up @@ -86,11 +87,24 @@ public function updateProfilePic(Request $request): RedirectResponse
try {
$file = $request->file('image');
$filename = ImageController::sanatizeImageFilename($file);

$subdirectory = ImageController::$avatarImagesSubdirectory;
$temporaryPath = sys_get_temp_dir() . '/' . $filename;
ImageManager::gd()
->read($file)
->resize(150, 150)
->save(ImageController::getImagePublicPath(ImageController::$avatarImagesSubdirectory, $filename));
->save($temporaryPath);

if (config('filesystems.default') === 's3') {
$s3Path = ImageController::$imagesDir . "/$subdirectory/$filename";
$success = Storage::disk('s3')->put($s3Path, file_get_contents($temporaryPath));
unlink($temporaryPath);
if (!$success) {
throw new \Exception();
}
} else {
$localPath = ImageController::getImagePublicPath($subdirectory, $filename);
rename($temporaryPath, $localPath);
}

$image = new Image();
$image->subdirectory = ImageController::$avatarImagesSubdirectory;
Expand All @@ -102,8 +116,8 @@ public function updateProfilePic(Request $request): RedirectResponse
$user->save();

session()->flash('message', 'Avatar updated successfully.');
} catch (\Exception) {
session()->flash('error', 'Avatar update failed! Please try again.');
} catch (\Exception $error) {
session()->flash('error', 'Avatar upload failed, please try again.');
}

return redirect()->back();
Expand All @@ -121,7 +135,16 @@ public function delete(Request $request, int $id): RedirectResponse
$user = User::find($id);
try {
$image = Image::findOrFail($user->image_id);
unlink(ImageController::getImagePublicPath($image->subdirectory, $image->filename));
if (config('filesystems.default') === 's3') {
$s3Path = ImageController::$imagesDir . "/$image->subdirectory/$image->filename";
$success = Storage::disk('s3')->delete($s3Path);
if (!$success) {
throw new \Exception();
}
} else {
$localPath = ImageController::getImagePublicPath($image->subdirectory, $image->filename);
unlink($localPath);
}
$image->delete();
} catch (ModelNotFoundException $e) {
// Don't delete an image that doesn't exist
Expand Down
1 change: 1 addition & 0 deletions src/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"laravel/framework": "11.34",
"laravel/tinker": "^2.9",
"laravel/ui": "^4.5",
"league/flysystem-aws-s3-v3": "^3.0",
"sentry/sentry-laravel": "^4.5"
},
"require-dev": {
Expand Down
Loading

0 comments on commit 3096dc5

Please sign in to comment.