rank < (int) setting('min_staff_rank', 7)) { abort(403, 'Forbidden'); } } public function search(Request $request) { $this->checkAdmin(); try { $q = mb_substr(strip_tags((string) $request->input('q', '')), 0, 100); $type = $request->input('type', ''); $page = max(1, (int) $request->input('page', 1)); $limit = min(50, max(1, (int) $request->input('limit', 20))); $query = DB::table('items_base'); if ($q !== '' && $q !== '0') { $query->where(function ($w) use ($q) { $w->where('item_name', 'like', "%{$q}%") ->orWhere('public_name', 'like', "%{$q}%") ->orWhere('id', '=', $q); }); } if ($type) { $query->where('type', $type); } $total = $query->count(); $items = $query ->orderBy('id') ->skip(($page - 1) * $limit) ->take($limit) ->get() ->map(fn ($r) => [ 'id' => $r->id, 'spriteId' => $r->sprite_id, 'itemName' => $r->item_name, 'publicName' => $r->public_name, 'type' => $r->type, 'width' => $r->width, 'length' => $r->length, 'stackHeight' => $r->stack_height, 'allowStack' => (bool) $r->allow_stack, 'allowWalk' => (bool) $r->allow_walk, 'allowSit' => (bool) $r->allow_sit, 'allowLay' => (bool) $r->allow_lay, 'interactionType' => $r->interaction_type, 'interactionModesCount' => $r->interaction_modes_count, ]); return response()->json([ 'items' => $items, 'total' => $total, 'page' => $page, ]); } catch (\Exception) { Log::error('[FurniEditor] Search failed', ['error' => 'Actie mislukt']); return response()->json(['error' => 'Zoeken mislukt'], 500); } } public function detail(Request $request) { $this->checkAdmin(); try { $id = (int) $request->input('id'); if ($id === 0) { return response()->json(['error' => 'Missing id'], 400); } $item = DB::table('items_base')->where('id', $id)->first(); if (! $item) { return response()->json(['error' => 'Item not found'], 404); } $catalog = DB::table('catalog_items') ->whereRaw('FIND_IN_SET(?, item_ids)', [$id]) ->get() ->map(fn ($r) => [ 'id' => $r->id, 'catalogName' => $r->catalog_name, 'costCredits' => $r->cost_credits, 'costPoints' => $r->cost_points, 'pointsType' => $r->points_type, 'pageId' => $r->page_id, 'pageName' => $r->page_id ? DB::table('catalog_pages')->where('id', $r->page_id)->value('caption') ?? '' : '', ]); return response()->json([ 'item' => [ 'id' => $item->id, 'spriteId' => $item->sprite_id, 'itemName' => $item->item_name, 'publicName' => $item->public_name, 'type' => $item->type, 'width' => $item->width, 'length' => $item->length, 'stackHeight' => $item->stack_height, 'allowStack' => (bool) $item->allow_stack, 'allowWalk' => (bool) $item->allow_walk, 'allowSit' => (bool) $item->allow_sit, 'allowLay' => (bool) $item->allow_lay, 'allowGift' => (bool) $item->allow_gift, 'allowTrade' => (bool) $item->allow_trade, 'allowRecycle' => (bool) $item->allow_recycle, 'allowMarketplaceSell' => (bool) $item->allow_marketplace_sell, 'allowInventoryStack' => (bool) $item->allow_inventory_stack, 'interactionType' => $item->interaction_type, 'interactionModesCount' => $item->interaction_modes_count, 'vendingIds' => $item->vending_ids, 'multiheight' => $item->multiheight, 'customparams' => $item->customparams, 'effectIdMale' => $item->effect_id_male, 'effectIdFemale' => $item->effect_id_female, 'clothingOnWalk' => $item->clothing_on_walk, 'description' => '', 'usageCount' => 0, ], 'catalogItems' => $catalog, 'furniDataEntry' => null, ]); } catch (\Exception) { return response()->json(['error' => 'Kan item niet laden'], 500); } } public function update(Request $request) { $this->checkAdmin(); try { $id = (int) $request->input('id'); if ($id === 0) { return response()->json(['error' => 'Missing id'], 400); } $data = $request->json()->all(); $update = []; $map = [ 'itemName' => ['item_name', 'string', 100], 'publicName' => ['public_name', 'string', 100], 'spriteId' => ['sprite_id', 'integer', 0], 'width' => ['width', 'integer', 0], 'length' => ['length', 'integer', 0], 'stackHeight' => ['stack_height', 'integer', 0], 'allowStack' => ['allow_stack', 'boolean', 0], 'allowWalk' => ['allow_walk', 'boolean', 0], 'allowSit' => ['allow_sit', 'boolean', 0], 'allowLay' => ['allow_lay', 'boolean', 0], 'allowGift' => ['allow_gift', 'boolean', 0], 'allowTrade' => ['allow_trade', 'boolean', 0], 'allowRecycle' => ['allow_recycle', 'boolean', 0], 'allowMarketplaceSell' => ['allow_marketplace_sell', 'boolean', 0], 'allowInventoryStack' => ['allow_inventory_stack', 'boolean', 0], 'interactionType' => ['interaction_type', 'string', 50], 'interactionModesCount' => ['interaction_modes_count', 'integer', 0], 'vendingIds' => ['vending_ids', 'string', 255], 'multiheight' => ['multiheight', 'string', 255], 'customparams' => ['customparams', 'string', 255], 'effectIdMale' => ['effect_id_male', 'integer', 0], 'effectIdFemale' => ['effect_id_female', 'integer', 0], 'clothingOnWalk' => ['clothing_on_walk', 'string', 255], ]; foreach ($map as $key => [$col, $type, $maxLen]) { if (! array_key_exists($key, $data)) { continue; } $val = $data[$key]; if ($type === 'string') { $val = mb_substr(strip_tags((string) $val), 0, $maxLen); } elseif ($type === 'integer') { $val = (int) $val; } elseif ($type === 'boolean') { $val = (bool) $val; } $update[$col] = $val; } if ($update === []) { return response()->json(['error' => 'No fields to update'], 400); } DB::table('items_base')->where('id', $id)->update($update); Log::info('[Audit] FurniEditor update', [ 'user_id' => Auth::id(), 'item_id' => $id, 'fields' => array_keys($update), ]); return response()->json(['success' => true]); } catch (\Exception) { return response()->json(['error' => 'Actie mislukt'], 500); } } public function create(Request $request) { $this->checkAdmin(); try { $data = $request->json()->all(); $id = DB::table('items_base')->insertGetId([ 'sprite_id' => $data['spriteId'] ?? 0, 'item_name' => $data['itemName'] ?? '', 'public_name' => $data['publicName'] ?? '', 'type' => $data['type'] ?? 's', 'width' => $data['width'] ?? 1, 'length' => $data['length'] ?? 1, 'stack_height' => $data['stackHeight'] ?? 0, 'allow_stack' => $data['allowStack'] ?? false, 'allow_walk' => $data['allowWalk'] ?? false, 'allow_sit' => $data['allowSit'] ?? false, 'allow_lay' => $data['allowLay'] ?? false, 'allow_gift' => $data['allowGift'] ?? true, 'allow_trade' => $data['allowTrade'] ?? true, 'allow_recycle' => $data['allowRecycle'] ?? false, 'allow_marketplace_sell' => $data['allowMarketplaceSell'] ?? false, 'allow_inventory_stack' => $data['allowInventoryStack'] ?? true, 'interaction_type' => $data['interactionType'] ?? 'default', 'interaction_modes_count' => $data['interactionModesCount'] ?? 1, ]); Log::info('[Audit] FurniEditor create', [ 'user_id' => Auth::id(), 'new_id' => $id, ]); return response()->json(['id' => $id]); } catch (\Exception) { return response()->json(['error' => 'Actie mislukt'], 500); } } public function delete(Request $request) { $this->checkAdmin(); try { $id = (int) $request->input('id'); if ($id === 0) { return response()->json(['error' => 'Missing id'], 400); } DB::table('items_base')->where('id', $id)->delete(); Log::warning('[Audit] FurniEditor delete', [ 'user_id' => Auth::id(), 'deleted_id' => $id, ]); return response()->json(['success' => true]); } catch (\Exception) { return response()->json(['error' => 'Actie mislukt'], 500); } } public function interactions(Request $request) { $this->checkAdmin(); try { $rows = DB::table('items_base') ->select('interaction_type') ->groupBy('interaction_type') ->orderBy('interaction_type') ->pluck('interaction_type'); return response()->json(['interactions' => $rows]); } catch (\Exception) { return response()->json(['error' => 'Actie mislukt'], 500); } } public function bySprite(Request $request) { $this->checkAdmin(); try { $spriteId = (int) $request->input('spriteId'); if ($spriteId === 0) { return response()->json(['error' => 'Missing spriteId'], 400); } $item = DB::table('items_base')->where('sprite_id', $spriteId)->first(); if (! $item) { return response()->json(['error' => 'Item not found'], 404); } return response()->json(['id' => $item->id]); } catch (\Exception) { return response()->json(['error' => 'Actie mislukt'], 500); } } }