Add image upload support to document editor
- Create ImageUploadController to handle image uploads - Store images in storage/app/public/images with UUID filenames - Integrate with EasyMDE editor for drag-drop, paste, and toolbar upload - Use original filename as alt text in markdown 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
54
src/app/Http/Controllers/ImageUploadController.php
Normal file
54
src/app/Http/Controllers/ImageUploadController.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ImageUploadController extends Controller
|
||||
{
|
||||
/**
|
||||
* Handle image upload from EasyMDE editor
|
||||
*/
|
||||
public function upload(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'image' => [
|
||||
'required',
|
||||
'file',
|
||||
'mimes:jpeg,jpg,png,gif,webp',
|
||||
'max:2048', // 2MB
|
||||
],
|
||||
]);
|
||||
|
||||
$file = $request->file('image');
|
||||
|
||||
// Get original filename without extension for alt text
|
||||
$originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
|
||||
|
||||
// Generate unique filename: YYYY/MM/uuid.extension
|
||||
$year = date('Y');
|
||||
$month = date('m');
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = Str::uuid() . '.' . $extension;
|
||||
$path = "images/{$year}/{$month}/{$filename}";
|
||||
|
||||
// Store to public disk
|
||||
Storage::disk('public')->putFileAs(
|
||||
"images/{$year}/{$month}",
|
||||
$file,
|
||||
$filename
|
||||
);
|
||||
|
||||
// Return URL for EasyMDE (use APP_URL)
|
||||
$url = asset('storage/' . $path);
|
||||
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'filePath' => $url,
|
||||
'altText' => $originalName,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -147,6 +147,42 @@ class="w-full"
|
||||
'guide'
|
||||
],
|
||||
status: ['lines', 'words', 'cursor'],
|
||||
// Image upload configuration
|
||||
uploadImage: true,
|
||||
imageMaxSize: 2 * 1024 * 1024, // 2MB
|
||||
imageAccept: 'image/png, image/jpeg, image/gif, image/webp',
|
||||
imageUploadFunction: (file, onSuccess, onError) => {
|
||||
const formData = new FormData();
|
||||
formData.append('image', file);
|
||||
|
||||
fetch('{{ route("images.upload") }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then(data => {
|
||||
throw new Error(data.message || 'Upload failed');
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Insert markdown with alt text directly
|
||||
const cm = this.editor.codemirror;
|
||||
const altText = data.data.altText || 'image';
|
||||
const url = data.data.filePath;
|
||||
const markdown = ``;
|
||||
cm.replaceSelection(markdown);
|
||||
})
|
||||
.catch(error => {
|
||||
onError(error.message || 'Failed to upload image');
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
this.editor.codemirror.on('change', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\LocaleController;
|
||||
use App\Http\Controllers\ImageUploadController;
|
||||
use App\Http\Controllers\Admin\UserController as AdminUserController;
|
||||
use App\Livewire\DocumentViewer;
|
||||
use App\Livewire\DocumentEditor;
|
||||
@@ -29,6 +30,9 @@
|
||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
||||
|
||||
// Image upload for editor
|
||||
Route::post('/images/upload', [ImageUploadController::class, 'upload'])->name('images.upload');
|
||||
});
|
||||
|
||||
// Admin routes
|
||||
|
||||
Reference in New Issue
Block a user