middleware('auth'); } public function store(Request $request, User $user) { if (Auth::id() !== $user->id) { abort(403); } $request->validate([ 'body' => 'nullable|string|max:2000', 'images' => 'nullable|array|max:10', 'images.*' => 'image|mimes:jpg,jpeg,png,webp,gif|max:8192', 'video_ids' => 'nullable|array|max:10', 'video_ids.*' => 'exists:videos,id', 'video_id' => 'nullable|exists:videos,id', 'image' => 'nullable|image|mimes:jpg,jpeg,png,webp,gif|max:8192', ]); $hasImages = $request->hasFile('images'); $hasVideoIds = $request->filled('video_ids'); $hasLegacyImg = $request->hasFile('image'); $hasLegacyVid = $request->filled('video_id'); if (! $request->body && ! $hasImages && ! $hasLegacyImg && ! $hasVideoIds && ! $hasLegacyVid) { return back()->withErrors(['body' => 'Post cannot be empty.']); } // Create post first — we need the ID as the folder name $post = Post::create([ 'user_id' => $user->id, 'body' => $request->body, 'video_id' => $request->video_id ?? null, ]); $nas = app(NasSyncService::class); $nasMode = $nas->isEnabled(); $postDir = $nas->resolvePostDir($post); // "users/{slug}/posts/{id}" if ($hasImages || $hasLegacyImg) { if ($nasMode) { // ── NAS primary: upload directly from PHP temp files ────────── $nas->mkdirp($postDir); if ($hasLegacyImg) { $file = $request->file('image'); $ext = $file->getClientOriginalExtension() ?: 'jpg'; $nasPath = "{$postDir}/0.{$ext}"; $nas->putFile($file->getRealPath(), $nasPath); $post->update(['image' => $nasPath]); } if ($hasImages) { foreach ($request->file('images') as $idx => $file) { $ext = $file->getClientOriginalExtension() ?: 'jpg'; $nasPath = "{$postDir}/" . ($idx + 1) . ".{$ext}"; $nas->putFile($file->getRealPath(), $nasPath); PostImage::create([ 'post_id' => $post->id, 'filename' => $nasPath, 'sort_order' => $idx, ]); } } } else { // ── Local storage: save inside the user's posts directory ───── $localDir = storage_path('app/' . $postDir); @mkdir($localDir, 0755, true); if ($hasLegacyImg) { $ext = $request->file('image')->getClientOriginalExtension() ?: 'jpg'; $filename = "0.{$ext}"; $request->file('image')->move($localDir, $filename); $post->update(['image' => "{$postDir}/{$filename}"]); } if ($hasImages) { foreach ($request->file('images') as $idx => $file) { $ext = $file->getClientOriginalExtension() ?: 'jpg'; $filename = ($idx + 1) . ".{$ext}"; $file->move($localDir, $filename); PostImage::create([ 'post_id' => $post->id, 'filename' => "{$postDir}/{$filename}", 'sort_order' => $idx, ]); } } } } if ($hasVideoIds) { foreach ($request->input('video_ids') as $idx => $videoId) { PostVideo::create([ 'post_id' => $post->id, 'video_id' => $videoId, 'sort_order' => $idx, ]); } } return back()->with('toast_success', 'Post shared!'); } public function destroy(Post $post) { if (Auth::id() !== $post->user_id && ! Auth::user()->isAdmin()) { abort(403); } $post->loadMissing('postImages'); $nas = app(NasSyncService::class); if ($nas->isEnabled()) { try { $nas->deleteNasPost($post); } catch (\Throwable) {} } // Always clean up local copies (handles both legacy flat and new structured format) $nas->deleteLocalPostImages($post); $post->delete(); return back()->with('toast_success', 'Post deleted.'); } public function react(Post $post) { $user = Auth::user(); $existing = $post->reactions()->where('user_id', $user->id)->first(); if ($existing) { $existing->delete(); $liked = false; } else { $post->reactions()->create(['user_id' => $user->id, 'type' => 'like']); $liked = true; } return response()->json([ 'liked' => $liked, 'count' => $post->reactions()->count(), ]); } }