summaryrefslogtreecommitdiffstats
path: root/templates/mods
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-07-29 01:03:24 +0200
committersalmonik <salmonik@tuta.io>2026-07-29 01:03:24 +0200
commit74d2ada067bd2fd9436710ab7ac260e0eddf1314 (patch)
tree1f791bb895a611d8e729a735ba098a647808a157 /templates/mods
parentcreate example config on startup (diff)
downloadminecraft-ui-74d2ada067bd2fd9436710ab7ac260e0eddf1314.tar.gz
minecraft-ui-74d2ada067bd2fd9436710ab7ac260e0eddf1314.zip
Add per-instance mod management with client-side detection
- New mod validation engine (src/mods.rs) with constant-pool class-file scanning: detects server-side content (block, tileentity, command, worldgen) vs client-only API (gui, renderer, forge/client) - 7 client-only mods correctly detected (AppleSkin, CraftingTweaks, Durability101, EnchantmentDescriptions, InventoryHUD, MouseTweaks, TrashSlot); 51 server-capable; 1 unknown - Routes + templating for mod upload (jar/zip), listing, delete, rescan - Multipart upload with 500MB limit (bypasses axum's default 2MB) - Per-instance mod summary card on edit page - Whitelist toggle checkbox on edit page (persisted, drives ENABLE_WHITELIST) - Dynamic Java image tag (java8/java17/java21) based on MC version - Class scan is informational-only; rejection based on metadata side=CLIENT - has_modpack flag cleanup on mod delete - Remove hx-boost from <body> (was corrupting multipart uploads)
Diffstat (limited to '')
-rw-r--r--templates/mods/list.html88
1 files changed, 88 insertions, 0 deletions
diff --git a/templates/mods/list.html b/templates/mods/list.html
new file mode 100644
index 0000000..b8aff1f
--- /dev/null
+++ b/templates/mods/list.html
@@ -0,0 +1,88 @@
1{% extends "base.html" %}
2{% block content %}
3<div class="page-header">
4 <h1>Mods: {{ instance_name }}</h1>
5 <a href="/instances/{{ instance_id }}/edit" class="btn">&larr; Back to Instance</a>
6</div>
7
8{% if let Some(msg) = flash %}
9<div class="flash flash-{{ msg.kind }}">{{ msg.text }}</div>
10{% endif %}
11
12<div class="card" style="margin-bottom:1rem">
13 <form action="/instances/{{ instance_id }}/mods" method="post" enctype="multipart/form-data" class="form-row">
14 <input type="file" name="mod_file" accept=".jar,.zip" required style="font-size:0.85rem">
15 <button type="submit" class="btn-ok">Upload & Validate</button>
16 </form>
17 <p style="margin-top:0.5rem;font-size:0.8rem;color:var(--fg-muted)">
18 Upload <code>.jar</code> files directly or a <code>.zip</code> containing multiple <code>.jar</code> files.
19 Mods containing <strong>only</strong> client-side classes will be rejected.
20 </p>
21</div>
22
23{% if mods.is_empty() %}
24<div class="card card-muted">
25 <p>No mods installed. Upload a mod to get started.</p>
26</div>
27{% else %}
28<table class="table">
29 <thead>
30 <tr>
31 <th>Name</th>
32 <th>Filename</th>
33 <th>Side</th>
34 <th>Status</th>
35 <th>Added</th>
36 <th></th>
37 </tr>
38 </thead>
39 <tbody>
40 {% for m in mods %}
41 <tr>
42 <td>
43 {% if let Some(name) = m.display_name %}
44 {{ name }}
45 {% else %}
46 <em>{{ m.filename }}</em>
47 {% endif %}
48 </td>
49 <td><code>{{ m.filename }}</code></td>
50 <td>
51 <span class="tag
52 {% if m.side == "BOTH" %}tag-ok{% endif %}
53 {% if m.side == "SERVER" %}tag-ok{% endif %}
54 {% if m.side == "CLIENT" %}tag-err{% endif %}
55 {% if m.side == "UNKNOWN" %}tag{% endif %}
56 ">{{ m.side }}</span>
57 </td>
58 <td>
59 {% if !m.accepted %}
60 <span class="status-badge status-error">Rejected</span>
61 {% else if m.side == "CLIENT" %}
62 <span class="status-badge status-warning">Warning</span>
63 {% else %}
64 <span class="status-badge status-running">Accepted</span>
65 {% endif %}
66 {% if let Some(reason) = m.rejection_reason %}
67 <div style="font-size:0.8rem;color:var(--err);margin-top:0.25rem">{{ reason }}</div>
68 {% endif %}
69 </td>
70 <td>{{ m.created_at }}</td>
71 <td>
72 <form action="/instances/{{ instance_id }}/mods/{{ m.id }}/delete" method="post" style="display:inline"
73 onsubmit="return confirm('Remove this mod?')">
74 <button type="submit" class="btn-danger btn-sm">Remove</button>
75 </form>
76 </td>
77 </tr>
78 {% endfor %}
79 </tbody>
80</table>
81{% endif %}
82
83<div style="margin-top:1rem">
84 <form action="/instances/{{ instance_id }}/mods/rescan" method="post" style="display:inline">
85 <button type="submit" class="btn">Rescan All Mods</button>
86 </form>
87</div>
88{% endblock %}