Skip to content

Conversation

@H1ghSyst3m
Copy link

@H1ghSyst3m H1ghSyst3m commented Jan 31, 2026

Overview

This PR adds comprehensive mod lifecycle management to the Minecraft Modrinth plugin. Currently, users can only install mods but have no way to update or remove them through the interface. This creates a frustrating user experience where they need to manually manage files outside the plugin.

Changes

Core Functionality

I've implemented update and uninstall capabilities for both mods and plugins. When users view their installed items, they'll now see action buttons that allow them to update to the latest version or remove items entirely. Both operations include confirmation modals to prevent accidental actions.

Metadata File

To make updates and uninstalls possible, I introduced a metadata tracking system using .modrinth-metadata.json. This file stores essential information about each installed mod (version, filename, installation date), which lets us determine when updates are available and ensures clean uninstallation

Localization & Documentation

I've added the necessary translation strings for English and German to support the new UI elements, and updated the README to document the new features

Implementation Notes

The metadata file approach was chosen over database storage to keep the plugin lightweight and make the data easily portable with the mod directory. The file is updated on each install/uninstall operation and read when checking for available updates.

Tested

  • Install, update, and uninstall workflows with metadata file verification
  • Uninstall properly removes both plugin file and metadata entry
  • Metadata file recreation when deleted (installed new plugin and file was correctly created)

Version

Bumped to 1.1.0 to reflect the new feature set.

Fixes Issues

Closes #62

Summary by CodeRabbit

  • New Features

    • Full mod lifecycle: install, update, uninstall; per-mod status, update detection, and seamless install/uninstall flows
    • Local metadata tracking (.modrinth-metadata.json) to prevent duplicates and record project IDs, versions, filenames, and install dates
    • New page filters: All / Installed; view toggles and installed-only listing
  • Documentation

    • README expanded with full mod management workflow, features, usage, and multilingual notes
    • Updated user-facing strings (EN/DE) for install/update/uninstall flows, modals, and notifications
  • Chores

    • Version bumped to 1.1.0

✏️ Tip: You can customize this high-level summary in your review settings.

Introduces update and uninstall actions for Minecraft mods/plugins, with UI enhancements for status tracking and confirmation dialogs. Adds persistent metadata management via a .modrinth-metadata.json file to track installed mods, versions, and installation dates. Updates translations, documentation, and bumps plugin version to 1.1.0.
Deleted 'download' related keys from both German and English language files as they are no longer needed.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Adds install/update/uninstall mod management with persistent .modrinth-metadata.json, UI/localization for new actions/modals/notifications, per-project version caching and helpers, service APIs for metadata I/O, and bumps plugin version to 1.1.0.

Changes

Cohort / File(s) Summary
Documentation & Versioning
minecraft-modrinth/README.md, minecraft-modrinth/plugin.json
README expanded to document browsing, install/update/uninstall flows and metadata tracking; plugin.json version bumped to 1.1.0 and description updated.
Localization
minecraft-modrinth/lang/en/strings.php, minecraft-modrinth/lang/de/strings.php
Replaced download label with install, installed, update, uninstall; added modals entries for update/uninstall confirmations; extended notifications for install/update/uninstall success/failure; added page filters (view_all, view_installed, mod_unavailable).
UI Page, Actions & Caching
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php
Added active view toggle (all/installed), installed mods metadata state and versions cache; replaced download flow with install/update/installed/uninstall actions; added filename validation, install/update/uninstall flows with rollback/cleanup, HTTP file deletions, cache invalidation, notifications, queryString persistence, and helper methods.
Service Layer: Metadata Management
minecraft-modrinth/src/Services/MinecraftModrinthService.php
Added metadata file path helper and public APIs to read/write/remove/query installed mods (getInstalledModsMetadata, saveModMetadata, removeModMetadata, getInstalledMod, getInstalledMods, isUpdateAvailable, getInstalledModsFromModrinth); manages .modrinth-metadata.json I/O, validation, timestamps, and update detection.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Page as MinecraftModrinthProjectPage
    participant Service as MinecraftModrinthService
    participant API as Modrinth API
    participant FileRepo as DaemonFileRepository
    participant Server as Server

    Note over User,Server: Install Mod Flow
    User->>Page: Click Install
    Page->>API: Fetch latest version & files
    API-->>Page: Version & file info
    Page->>Page: Select primary file & validate filename
    Page->>API: Download file content
    API-->>Page: File content
    Page->>FileRepo: Save file to server
    FileRepo->>Server: Persist mod file
    Page->>Service: Save mod metadata (.modrinth-metadata.json)
    Service->>FileRepo: Write metadata
    Page->>Page: Clear caches & emit notification
    Page-->>User: Install success

    Note over User,Server: Update Mod Flow
    User->>Page: Click Update
    Page->>Service: Load installed metadata
    Page->>API: Fetch available versions
    API-->>Page: Versions list
    Page->>Page: Detect newer version, confirm with user
    User-->>Page: Approve
    Page->>API: Download new file
    API-->>Page: New file
    Page->>FileRepo: Save new file and delete old file
    FileRepo->>Server: Replace files
    Page->>Service: Update metadata
    Page-->>User: Update success

    Note over User,Server: Uninstall Mod Flow
    User->>Page: Click Uninstall
    Page->>User: Confirm uninstall
    User-->>Page: Approve
    Page->>Service: Remove metadata entry
    Service->>FileRepo: Update metadata file
    Page->>FileRepo: Delete mod file from server
    Page->>Page: Clear caches & emit notification
    Page-->>User: Uninstall success
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

Suggested Reviewers

  • Boy132
  • lajczi

Poem

🐇 I hopped through code with nimble paws,

I tracked each mod with JSON laws.
Install, update, or say goodbye,
Version 1.1 — a joyful cry,
Carrot cheers for every successful try!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: implementing mod/plugin management with update and uninstall capabilities. It is concise, clear, and directly reflects the primary objective of the PR.
Linked Issues check ✅ Passed The PR fully addresses issue #62 requirements: it tracks installed mods using .modrinth-metadata.json, implements update detection by comparing installed vs. available versions, provides UI mechanisms for safe updates that respect compatibility, and adds uninstall functionality.
Out of Scope Changes check ✅ Passed All changes are within scope: metadata tracking system, install/update/uninstall actions, view filtering for installed mods, language string updates, README documentation, and version bump to 1.1.0 all directly support the mod management objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🧹 Recent nitpick comments
minecraft-modrinth/src/Services/MinecraftModrinthService.php (1)

120-132: Minor optimization opportunity in installed mod lookup.

The nested loop to find $installedMod for each $projectId is O(n×m). For larger metadata sets, consider pre-building a lookup map. However, given the 20-item page limit, the current implementation is acceptable.

♻️ Optional: Pre-build lookup map
+        $installedModMap = [];
+        foreach ($installedMods as $mod) {
+            $installedModMap[$mod['project_id']] = $mod;
+        }
+
         $results = [];
         foreach ($pageIds as $projectId) {
-            $installedMod = null;
-            foreach ($installedMods as $mod) {
-                if ($mod['project_id'] === $projectId) {
-                    $installedMod = $mod;
-                    break;
-                }
-            }
+            $installedMod = $installedModMap[$projectId] ?? null;

             if (!$installedMod) {
                 continue;
             }
📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eef0dd2 and 2da4031.

📒 Files selected for processing (4)
  • minecraft-modrinth/lang/de/strings.php
  • minecraft-modrinth/lang/en/strings.php
  • minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php
  • minecraft-modrinth/src/Services/MinecraftModrinthService.php
🧰 Additional context used
🧬 Code graph analysis (2)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (2)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (3)
  • fromServer (29-45)
  • getLabel (13-19)
  • getFolder (21-27)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (7)
  • getInstalledModsMetadata (219-258)
  • getInstalledMod (346-357)
  • getModrinthVersions (165-202)
  • getInstalledModsFromModrinth (77-162)
  • getModrinthProjects (26-71)
  • saveModMetadata (260-313)
  • removeModMetadata (315-343)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (3)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (2)
  • fromServer (29-45)
  • getFolder (21-27)
minecraft-modrinth/src/Enums/MinecraftLoader.php (1)
  • fromServer (23-30)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (3)
  • getInstalledModsMetadata (95-107)
  • content (576-620)
  • getInstalledMod (110-121)
🔇 Additional comments (16)
minecraft-modrinth/lang/de/strings.php (2)

55-67: LGTM! German notification strings are well-structured.

The notification strings are grammatically correct and maintain consistency with the English counterpart. The placeholders (:name, :version) are properly included where needed.

Note: uninstall_success intentionally lacks a _body variant (consistent with the English file), which is acceptable since the uninstall success notification uses the mod title as the body content directly in the UI code.


19-53: LGTM! New page, actions, and modals strings are well-localized.

The German translations are accurate and maintain proper placeholder consistency (:old_version, :new_version, :name) with the English source strings.

minecraft-modrinth/lang/en/strings.php (1)

1-68: LGTM! English strings are well-structured and complete.

The language file properly supports the new install/update/uninstall workflow with appropriate placeholders for dynamic content. The structure is consistent with the German translations file.

minecraft-modrinth/src/Services/MinecraftModrinthService.php (5)

73-111: LGTM! Bulk retrieval with pagination and caching is well-implemented.

The getInstalledModsFromModrinth method correctly handles:

  • Empty input early return
  • Pagination with offset calculation
  • Cache key generation using MD5 hash
  • API timeout configuration
  • Error handling with fallback to empty array

182-195: LGTM! Defensive sorting ensures consistent latest version detection.

The sorting by date_published in descending order correctly ensures the latest version is always at index 0, regardless of API response ordering. The conditional check and null coalescing provide good defensive coding.


218-258: LGTM! Robust metadata reading with thorough validation.

The validation logic correctly:

  • Handles missing or malformed metadata files gracefully (returns [])
  • Validates structure with required keys check
  • Filters out invalid entries without breaking the entire operation

The silent exception catch at line 255 is appropriate since the metadata file may not exist yet for new installations.


260-343: LGTM! Metadata persistence methods are well-implemented.

Both saveModMetadata and removeModMetadata:

  • Correctly use ->failed() to check write response (addresses past review)
  • Handle exceptions gracefully with logging
  • Use atomic read-modify-write pattern
  • Properly deduplicate entries before adding new ones

359-385: LGTM! Helper methods are simple and correct.

  • isUpdateAvailable correctly relies on the sorted versions array (latest at index 0)
  • getInstalledMods returns lowercase filenames for case-insensitive comparisons
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (8)

35-59: LGTM! View state management and URL persistence are well-implemented.

The multi-view support with VIEW_ALL and VIEW_INSTALLED constants, combined with queryString() for URL state persistence, provides a clean UX. The caches for installed mods and versions avoid redundant API/file calls.


150-160: LGTM! Filename validation provides path traversal protection.

The validateFilename method correctly:

  • Rejects empty strings
  • Detects path separators (/ and \)
  • Returns basename as additional safety

This addresses the past review concern about validating filenames from Modrinth.


179-190: LGTM! View-based table records switching is well-implemented.

The VIEW_INSTALLED branch correctly uses local metadata as the source of truth and fetches Modrinth project data in bulk, while VIEW_ALL uses the standard search API. Both paths return proper LengthAwarePaginator instances.


219-312: LGTM! Install action with rollback is robust.

The install action correctly:

  1. Validates filename before download
  2. Downloads file to server
  3. Saves metadata
  4. Rolls back (deletes file) if metadata save fails
  5. Invalidates both caches on success and failure

This addresses past review concerns about orphaned installs and cache consistency.


313-444: LGTM! Update action maintains consistency with proper ordering.

The update action correctly:

  1. Downloads new version first
  2. Updates metadata to point to new file
  3. Rolls back new file if metadata save fails
  4. Only deletes old file after metadata is successfully saved

This addresses past review concerns about metadata desync during updates.


466-535: LGTM! Uninstall action with metadata-first removal is correct.

The uninstall action correctly removes metadata before deleting the file. As noted in the comment, this is the safer approach: if file deletion fails, the file exists but isn't tracked (can be manually cleaned), versus metadata pointing to a non-existent file.

The refreshIfInInstalledView() call ensures the UI updates when viewing installed mods.


539-573: LGTM! Header actions with view toggles are well-implemented.

The header actions correctly:

  • Guard against null server type
  • Reset table state before switching views
  • Trigger refresh to update UI
  • Provide folder access link

576-620: LGTM! Content schema handles null type safely.

The content schema correctly handles the case where server type cannot be determined, displaying "Unknown" or fallback labels instead of crashing.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (1)

18-20: ⚠️ Potential issue | 🟠 Major

Remove unused Section import to fix Pint failure.

The lint failure (no_unused_imports) points to this unused import.

🧹 Suggested fix
-use Filament\Schemas\Components\Section;
🤖 Fix all issues with AI agents
In
`@minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php`:
- Around line 213-232: The primaryFile['filename'] from Modrinth must be
validated/sanitized before using it in fileRepository->pull and
MinecraftModrinth::saveModMetadata; call the existing validateFilename() (or
equivalent sanitizer) on $primaryFile['filename'] inside the block that gets
$primaryFile (around getPrimaryFile(...) and before
$fileRepository->setServer(...)->pull(...)) and use the sanitized filename for
both the pull destination and the saved metadata (replace uses of
$primaryFile['filename'] with the sanitized variable); apply the same change to
the similar logic in the other occurrence referenced (lines ~319-351) so no
unvalidated filename is persisted or used for file paths.

In `@minecraft-modrinth/src/Services/MinecraftModrinthService.php`:
- Around line 188-196: The code checks the result of
$fileRepository->setServer($server)->putContent(...) against false, but
DaemonFileRepository::putContent() returns an Illuminate\Http\Client\Response,
so replace the boolean check with the response failure check: call ->failed() on
the $result (i.e. if ($result->failed()) return false;). Update both places
where metadata is written (the blocks that call getMetadataFilePath($server) and
then putContent via $fileRepository->setServer(...)->putContent) to use
$result->failed() instead of === false.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5332a38 and eb2e0c3.

📒 Files selected for processing (6)
  • minecraft-modrinth/README.md
  • minecraft-modrinth/lang/de/strings.php
  • minecraft-modrinth/lang/en/strings.php
  • minecraft-modrinth/plugin.json
  • minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php
  • minecraft-modrinth/src/Services/MinecraftModrinthService.php
🧰 Additional context used
🧬 Code graph analysis (1)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (2)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (2)
  • fromServer (29-45)
  • getFolder (21-27)
minecraft-modrinth/src/Enums/MinecraftLoader.php (1)
  • fromServer (23-30)
🪛 GitHub Actions: Lint
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php

[error] 1-1: Pint: 'no_unused_imports' issue detected. Remove unused imports in MinecraftModrinthProjectPage.php.

🪛 GitHub Check: PHPStan (8.2)
minecraft-modrinth/src/Services/MinecraftModrinthService.php

[failure] 224-224:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.


[failure] 194-194:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.

🪛 GitHub Check: PHPStan (8.3)
minecraft-modrinth/src/Services/MinecraftModrinthService.php

[failure] 224-224:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.


[failure] 194-194:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.

🪛 GitHub Check: PHPStan (8.4)
minecraft-modrinth/src/Services/MinecraftModrinthService.php

[failure] 224-224:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.


[failure] 194-194:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.

🪛 GitHub Check: PHPStan (8.5)
minecraft-modrinth/src/Services/MinecraftModrinthService.php

[failure] 224-224:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.


[failure] 194-194:
Strict comparison using === between Illuminate\Http\Client\Response and false will always evaluate to false.

🔇 Additional comments (7)
minecraft-modrinth/plugin.json (1)

5-6: Version/description update matches the new feature set.

minecraft-modrinth/lang/en/strings.php (1)

38-63: Strings align cleanly with the new install/update/uninstall flow.

minecraft-modrinth/lang/de/strings.php (1)

38-63: German translations are consistent with the new action set.

minecraft-modrinth/src/Services/MinecraftModrinthService.php (2)

105-154: Metadata file path + validation flow looks solid.


236-277: Helper accessors + update check are clear and well-scoped.

minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (1)

36-148: Helper methods for metadata caching and filename validation look good.

minecraft-modrinth/README.md (1)

3-41: Docs clearly reflect the new install/update/uninstall lifecycle.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@H1ghSyst3m
Copy link
Author

I also thought about adding a filter to only show installed mods/plugins, but I'm not sure whether I should just add the “Installed only” filter or also add “Updates available.”

Added filename validation to prevent path traversal when handling files from the Modrinth API. Updated file save logic to check for failed responses instead of boolean false, improving reliability and security.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (1)

20-20: ⚠️ Potential issue | 🟡 Minor

Remove unused import Section.

The pipeline failure indicates an unused import. The Filament\Schemas\Components\Section import is not used in this file.

🧹 Proposed fix
 use Filament\Schemas\Components\Grid;
-use Filament\Schemas\Components\Section;
 use Filament\Schemas\Schema;
🤖 Fix all issues with AI agents
In `@minecraft-modrinth/src/Services/MinecraftModrinthService.php`:
- Around line 105-110: getMetadataFilePath currently calls
ModrinthProjectType::fromServer($server)->getFolder() without checking for null,
which can cause a fatal error when fromServer returns null; update
getMetadataFilePath in MinecraftModrinthService.php to handle the null case by
first assigning $type = ModrinthProjectType::fromServer($server), then if $type
is null either throw a clear exception (e.g., InvalidArgumentException with a
message referencing the Server) or return a sensible default path, otherwise
call $type->getFolder() to build and return the metadata path.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb2e0c3 and f2d3bb3.

📒 Files selected for processing (2)
  • minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php
  • minecraft-modrinth/src/Services/MinecraftModrinthService.php
🧰 Additional context used
🧬 Code graph analysis (2)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (2)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (2)
  • fromServer (29-45)
  • getFolder (21-27)
minecraft-modrinth/src/Enums/MinecraftLoader.php (1)
  • fromServer (23-30)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (2)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (5)
  • getInstalledModsMetadata (113-154)
  • getInstalledMod (237-248)
  • getModrinthVersions (74-103)
  • saveModMetadata (156-204)
  • removeModMetadata (206-234)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (2)
  • fromServer (29-45)
  • getFolder (21-27)
🪛 GitHub Actions: Lint
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php

[error] 1-1: Pint test failed due to no_unused_imports issue. Unused imports detected in MinecraftModrinthProjectPage.php. Step: vendor/bin/pint --test. Process exited with code 1.

🔇 Additional comments (9)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (4)

112-154: Well-structured metadata validation.

The validation approach using array_flip and array_diff_key is efficient. Good defensive coding with structure validation and safe fallback on exceptions.


156-204: Metadata save logic is correct.

The approach of removing existing entries before adding the new one ensures clean updates. The $response->failed() check correctly handles the Response object now.


250-263: LGTM!

Version comparison by version_id is reliable since Modrinth returns versions newest-first.


265-278: LGTM!

Clean backward-compatible helper that extracts and normalizes filenames.

minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (5)

79-147: Well-designed caching and validation helpers.

The request-scoped caching via properties avoids redundant API/file reads. The validateFilename method properly guards against path traversal by checking for separators and using basename().


185-263: Install action is well-implemented.

Good defensive coding: validates version structure, sanitizes filename before use, and invalidates cache on both success and failure paths. The download-before-metadata-save order is correct.


264-384: Update action handles file replacement safely.

The download-first approach at line 334 prevents leaving the mod in a broken state if the download fails. Both old and new filenames are validated, and the old file is only deleted when filenames differ (line 338).


385-406: LGTM!

The installed badge correctly shows for up-to-date mods and gracefully handles the case when version info is unavailable.


407-469: Uninstall action follows a safe ordering.

The comment on lines 431-433 correctly explains why metadata is removed first: an orphaned file is safer than metadata pointing to a non-existent file. Filename validation prevents path traversal on the delete operation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Added validation to ensure the server supports Modrinth mods or plugins before performing operations. Exceptions are now thrown when the project type is not available, improving error handling and preventing undefined behavior.
Deleted the import of Filament\Schemas\Components\Section from MinecraftModrinthProjectPage.php as it was not used in the file.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (1)

18-21: ⚠️ Potential issue | 🟡 Minor

Remove unused Section import to satisfy lint.
The lint job reports no_unused_imports, and Filament\Schemas\Components\Section isn’t referenced here.

🧹 Remove unused import
-use Filament\Schemas\Components\Section;
🤖 Fix all issues with AI agents
In
`@minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php`:
- Around line 187-270: The install action can leave a downloaded JAR on disk if
MinecraftModrinth::saveModMetadata(...) returns false; after calling
$fileRepository->setServer($server)->pull(...), ensure you delete the downloaded
file on metadata-save failure (use the same $fileRepository with $server and the
target folder from $type->getFolder() and the validated $safeFilename) before
throwing/returning an error so the filesystem and UI stay consistent; update the
closure that calls DaemonFileRepository->pull and
MinecraftModrinth::saveModMetadata to perform this rollback cleanup when save
fails and then surface the failure to trigger the existing catch/notification
path.
- Around line 271-395: The current update action deletes the old file after
downloading the new file but before saving metadata, which can leave metadata
pointing to a deleted file if MinecraftModrinth::saveModMetadata fails; change
the flow in the Action::make('update')->action(...) closure to: 1) download the
new file with $fileRepository->pull as now, 2) call
MinecraftModrinth::saveModMetadata(...) immediately to update metadata to the
new filename/version, 3) only after a successful save delete the old file via
Http::daemon(...)->post(...), and 4) if save fails, rollback by deleting the
newly downloaded file (use the same $folder and $safeNewFilename validated via
validateFilename) and rethrow/handle the exception; also ensure
installedModsMetadata is invalidated consistently after successful update or on
rollback failure.

In `@minecraft-modrinth/src/Services/MinecraftModrinthService.php`:
- Around line 257-270: The isUpdateAvailable method assumes
$availableVersions[0] is newest; instead sort $availableVersions by the version
published timestamp (e.g. the version's "published" or "date_published" field)
in descending order before picking the latest, then compare
$installedMod['version_id'] to the sorted first element's 'id'; implement the
sort inside isUpdateAvailable (e.g. with usort) so the latest version is
deterministically selected.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2d3bb3 and 0b92108.

📒 Files selected for processing (2)
  • minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php
  • minecraft-modrinth/src/Services/MinecraftModrinthService.php
🧰 Additional context used
🧬 Code graph analysis (1)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (2)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (2)
  • fromServer (29-45)
  • getFolder (21-27)
minecraft-modrinth/src/Enums/MinecraftLoader.php (1)
  • fromServer (23-30)
🪛 GitHub Actions: Lint
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php

[error] 1-1: vendor/bin/pint --test: no_unused_imports violation in MinecraftModrinthProjectPage.php.

🔇 Additional comments (17)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (6)

105-116: Good guard for unsupported server types.
Clear exception prevents null dereferences and gives a helpful error.


119-160: Robust metadata validation.
Required-key filtering plus safe fallback keeps invalid metadata out of the UI.


163-210: Metadata write flow is clean.
Filtering existing entries before appending makes updates deterministic.


213-240: Clean removal path.


243-255: Straightforward lookup.


272-285: Back-compat filename list looks good.
Lowercasing helps keep comparisons stable.

minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (11)

36-40: Caching fields look good.


56-64: Graceful fallback label.


81-94: Lazy-load metadata cache is solid.


96-108: Simple lookup is fine.


110-119: Per-project version cache is fine.


122-135: Primary-file selection is clear.


137-149: Good filename sanitization guard.


397-418: Installed-state action is clear.


419-485: Uninstall flow is consistent.


490-507: Header action guard is solid.


509-549: Installed-count display update looks good.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php`:
- Around line 247-248: When clearing the installed mods metadata, also clear the
versions cache to avoid stale state: wherever you set
$this->installedModsMetadata = null (e.g. the occurrences at the shown
locations), also set $this->versionsCache = null in the same block; ensure every
success/failure cache reset that touches $this->installedModsMetadata includes
$this->versionsCache = null so both caches are reset together.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b92108 and eef0dd2.

📒 Files selected for processing (1)
  • minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php
🧰 Additional context used
🧬 Code graph analysis (1)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (4)
minecraft-modrinth/src/Enums/ModrinthProjectType.php (3)
  • fromServer (29-45)
  • getLabel (13-19)
  • getFolder (21-27)
minecraft-modrinth/src/Enums/MinecraftLoader.php (2)
  • fromServer (23-30)
  • getLabel (18-21)
minecraft-modrinth/src/Services/MinecraftModrinthService.php (4)
  • getInstalledModsMetadata (120-161)
  • getInstalledMod (244-255)
  • saveModMetadata (163-211)
  • removeModMetadata (213-241)
minecraft-modrinth/src/Facades/MinecraftModrinth.php (1)
  • MinecraftModrinth (19-25)
🔇 Additional comments (3)
minecraft-modrinth/src/Filament/Server/Pages/MinecraftModrinthProjectPage.php (3)

28-148: Nice helper/cache layer with filename sanitization.
Clear separation of metadata lookup, version caching, and filename validation keeps the action handlers much cleaner and safer.


396-417: Installed badge logic looks solid.
The visibility rules map cleanly to “installed + latest” vs “installed but outdated.”


494-547: Server-type guard additions are a good safety net.
Returning empty header actions and “unknown” states when unsupported avoids null issues and confusing UI.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@H1ghSyst3m H1ghSyst3m marked this pull request as draft January 31, 2026 04:54
Adds rollback logic to delete downloaded files if saving mod metadata fails during install or update, ensuring consistency. Moves old file deletion after successful metadata save during updates. Ensures UI cache is invalidated after install, update, or uninstall actions. Adds defensive sorting of Modrinth versions by publish date to guarantee latest version is first.
Introduces a new 'Installed' view for Modrinth projects, allowing users to filter and display only installed mods. Adds logic to fetch installed mods' data in bulk from the Modrinth API, gracefully handles unavailable mods by displaying a message, and updates language files for new UI labels. Also ensures author metadata is stored and displayed where available.
Updated type annotations and logic in both MinecraftModrinthProjectPage and MinecraftModrinthService to support an optional 'author' field in installed mod metadata arrays. This allows mod author information to be stored and retrieved when available.
@H1ghSyst3m H1ghSyst3m marked this pull request as ready for review February 1, 2026 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Minecraft Modrinth] Keep track of installed plugins/mods and add update functionality

1 participant