Tiles could vanish without ever being deleted:
- Editing an item merged the editor's user_id into every save, so
updating a visible item (e.g. a shared user_id=0 tile) silently
reassigned ownership and hid it from everyone else. user_id is now
set on create only, and excluded from update input in both Item and
Tag controllers since it is mass-assignable.
- Deleting a user left their items orphaned with a dangling user_id,
invisible to all users forever. The user's items are now hard-deleted
with the account, and a data migration reassigns already-orphaned
items to the admin user so previously "lost" tiles reappear.
- The Item global scope's ownership filter had an ungrouped orWhere,
breaking operator precedence in any query that adds further clauses.
Includes regression coverage for ownership on create/update, user
deletion cleanup, and the orphan-recovery migration.
ItemController::appload() was declared ': ?string', so its two error
branches that 'return response()->json([...], 404)' had the JsonResponse
coerced through Response::__toString() into a raw HTTP message served as
an HTTP 200 body. Widen the return type to
'\Illuminate\Http\JsonResponse|string|null' so those branches emit
real 404 JSON responses. The method body is unchanged, so the happy path
still returns the same JSON string and the frontend contract is preserved.
Flip the endpoint characterization test to assert the corrected 404.
get_stats/{id} fataled when the item id was missing and 500'd whenever an
enhanced app's livestats() threw - a broken or updated remote app definition
(e.g. Komga) took the whole request down, and the frontend then stopped
refreshing that tile entirely.
getStats now returns valid JSON (200) with an inactive/empty payload when the
item is missing, has no class, references a stale class, or throws, logging
the failure for diagnosis. The successful path is unchanged and returns the
livestats output verbatim.
Resolves#1558