summaryrefslogtreecommitdiffstats
path: root/templates
diff options
context:
space:
mode:
Diffstat (limited to 'templates')
-rw-r--r--templates/base.html50
-rw-r--r--templates/instances/edit.html349
-rw-r--r--templates/mods/list.html88
3 files changed, 383 insertions, 104 deletions
diff --git a/templates/base.html b/templates/base.html
index 3a92cd0..0af5d57 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -1,37 +1,35 @@
1<!DOCTYPE html> 1<!doctype html>
2<html lang="en"> 2<html lang="en">
3<head> 3 <head>
4 <meta charset="UTF-8"> 4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>Minecraft UI</title> 6 <title>Minecraft UI</title>
7 <script src="/static/htmx.min.js"></script> 7 <script src="/static/htmx.min.js"></script>
8 <link rel="stylesheet" href="/static/style.css"> 8 <link rel="stylesheet" href="/static/style.css" />
9</head> 9 </head>
10<body hx-boost="true"> 10 <body>
11 <nav> 11 <nav hx-boost="true">
12 <span class="brand">Minecraft UI</span> 12 <span class="brand">Minecraft UI</span>
13 <div class="nav-links"> 13 <div class="nav-links">
14 <a href="/">Dashboard</a> 14 <a href="/">Dashboard</a>
15 <a href="/instances">Instances</a> 15 <a href="/instances">Instances</a>
16 <a href="/players">Players</a> 16 <a href="/players">Players</a>
17 {% if is_superuser %} 17 {% if is_superuser %}
18 <a href="/accounts">Accounts</a> 18 <a href="/accounts">Accounts</a>
19 {% endif %} 19 {% endif %}
20 <span class="nav-user">{{ username }}</span> 20 <span class="nav-user">{{ username }}</span>
21 <form action="/logout" method="post" style="display:inline"> 21 <form action="/logout" method="post" style="display: inline">
22 <button type="submit" class="btn-link">Logout</button> 22 <button type="submit" class="btn-link">Logout</button>
23 </form> 23 </form>
24 </div> 24 </div>
25 </nav> 25 </nav>
26 26
27 {% if password_change_required %} 27 {% if password_change_required %}
28 <div class="flash flash-warn"> 28 <div class="flash flash-warn">
29 You must change your password before continuing. 29 You must change your password before continuing.
30 </div> 30 </div>
31 {% endif %} 31 {% endif %}
32 32
33 <main> 33 <main>{% block content %}{% endblock %}</main>
34 {% block content %}{% endblock %} 34 </body>
35 </main>
36</body>
37</html> 35</html>
diff --git a/templates/instances/edit.html b/templates/instances/edit.html
index fdfc0e1..2660461 100644
--- a/templates/instances/edit.html
+++ b/templates/instances/edit.html
@@ -1,94 +1,287 @@
1{% extends "base.html" %} 1{% extends "base.html" %} {% block content %}
2{% block content %}
3<h1>Edit: {{ instance.name }}</h1> 2<h1>Edit: {{ instance.name }}</h1>
4 3
5<div class="card card-muted"> 4<div class="card card-muted">
6 <div class="meta"> 5 <div class="meta">
7 <span><strong>Version:</strong> {{ instance.version }}</span> 6 <span><strong>Version:</strong> {{ instance.version }}</span>
8 <span><strong>Type:</strong> {{ instance.server_type }}</span> 7 <span><strong>Type:</strong> {{ instance.server_type }}</span>
9 <span><strong>World:</strong> {{ instance.world_source }}</span> 8 <span><strong>World:</strong> {{ instance.world_source }}</span>
10 {% if let Some(seed) = current_seed %} 9 {% if let Some(seed) = current_seed %}
11 <span><strong>Seed:</strong> {{ seed }}</span> 10 <span><strong>Seed:</strong> {{ seed }}</span>
12 {% endif %} 11 {% endif %} {% if instance.has_modpack %}
13 {% if instance.has_modpack %} 12 <span><strong>Modpack:</strong> Installed</span>
14 <span><strong>Modpack:</strong> Installed</span> 13 {% endif %}
15 {% endif %} 14 </div>
16 </div>
17</div> 15</div>
18 16
19<form action="/instances/{{ instance.id }}/edit" method="post" class="form-col"> 17<form action="/instances/{{ instance.id }}/edit" method="post" class="form-col">
20 <fieldset> 18 <fieldset>
21 <legend>Server Properties</legend> 19 <legend>Server Properties</legend>
22 <label> 20 <label>
23 Memory (MB) 21 Memory (MB)
24 <input type="number" name="memory_mb" value="{{ instance.memory_mb }}" min="512" max="{{ max_memory_mb }}" required> 22 <input
25 </label> 23 type="number"
26 <label> 24 name="memory_mb"
27 Difficulty 25 value="{{ instance.memory_mb }}"
28 <select name="difficulty"> 26 min="512"
29 <option value="0" {% if instance.difficulty == 0 %}selected{% endif %}>Peaceful</option> 27 max="{{ max_memory_mb }}"
30 <option value="1" {% if instance.difficulty == 1 %}selected{% endif %}>Easy</option> 28 required
31 <option value="2" {% if instance.difficulty == 2 %}selected{% endif %}>Normal</option> 29 />
32 <option value="3" {% if instance.difficulty == 3 %}selected{% endif %}>Hard</option> 30 </label>
33 </select> 31 <label>
34 </label> 32 Difficulty
35 <label> 33 <select name="difficulty">
36 Gamemode 34 <option
37 <select name="gamemode"> 35 value="0"
38 <option value="0" {% if instance.gamemode == 0 %}selected{% endif %}>Survival</option> 36 {%
39 <option value="1" {% if instance.gamemode == 1 %}selected{% endif %}>Creative</option> 37 if
40 <option value="2" {% if instance.gamemode == 2 %}selected{% endif %}>Adventure</option> 38 instance.difficulty=""
41 <option value="3" {% if instance.gamemode == 3 %}selected{% endif %}>Spectator</option> 39 ="0"
42 </select> 40 %}selected{%
43 </label> 41 endif
44 <label> 42 %}
45 Max Players 43 >
46 <input type="number" name="max_players" value="{{ instance.max_players }}" min="1" max="100"> 44 Peaceful
47 </label> 45 </option>
48 <label> 46 <option
49 <input type="checkbox" name="pvp" {% if instance.pvp %}checked{% endif %}> PvP enabled 47 value="1"
50 </label> 48 {%
51 <label> 49 if
52 MOTD 50 instance.difficulty=""
53 <input type="text" name="motd" value="{{ instance.motd }}" maxlength="59"> 51 ="1"
54 </label> 52 %}selected{%
55 </fieldset> 53 endif
54 %}
55 >
56 Easy
57 </option>
58 <option
59 value="2"
60 {%
61 if
62 instance.difficulty=""
63 ="2"
64 %}selected{%
65 endif
66 %}
67 >
68 Normal
69 </option>
70 <option
71 value="3"
72 {%
73 if
74 instance.difficulty=""
75 ="3"
76 %}selected{%
77 endif
78 %}
79 >
80 Hard
81 </option>
82 </select>
83 </label>
84 <label>
85 Gamemode
86 <select name="gamemode">
87 <option value="0" {% if instance.gamemode="" ="0" %}selected{% endif %}>
88 Survival
89 </option>
90 <option value="1" {% if instance.gamemode="" ="1" %}selected{% endif %}>
91 Creative
92 </option>
93 <option value="2" {% if instance.gamemode="" ="2" %}selected{% endif %}>
94 Adventure
95 </option>
96 <option value="3" {% if instance.gamemode="" ="3" %}selected{% endif %}>
97 Spectator
98 </option>
99 </select>
100 </label>
101 <label>
102 Max Players
103 <input
104 type="number"
105 name="max_players"
106 value="{{ instance.max_players }}"
107 min="1"
108 max="100"
109 />
110 </label>
111 <label>
112 <input
113 type="checkbox"
114 name="pvp"
115 {%
116 if
117 instance.pvp
118 %}checked{%
119 endif
120 %}
121 />
122 PvP enabled
123 </label>
124 <label>
125 <input
126 type="checkbox"
127 name="whitelist_enabled"
128 {%
129 if
130 instance.whitelist_enabled
131 %}checked{%
132 endif
133 %}
134 />
135 Whitelist enabled
136 </label>
137 <label>
138 MOTD
139 <input
140 type="text"
141 name="motd"
142 value="{{ instance.motd }}"
143 maxlength="59"
144 />
145 </label>
146 </fieldset>
56 147
57 <div class="actions"> 148 <div class="actions">
58 <button type="submit" class="btn-ok">Save Changes</button> 149 <button type="submit" class="btn-ok">Save Changes</button>
59 <a href="/instances" class="btn">Cancel</a> 150 <a href="/instances" class="btn">Cancel</a>
60 </div> 151 </div>
61</form> 152</form>
62 153
63<div class="card{% if instance.is_active %} card-muted{% endif %}" style="margin-top:1.5rem"> 154<div
64 <h3>World Management</h3> 155 class="card{% if instance.is_active %} card-muted{% endif %}"
156 style="margin-top: 1.5rem"
157>
158 <h3>World Management</h3>
65 159
66 {% if instance.is_active %} 160 {% if instance.is_active %}
67 <div class="flash flash-warn">Stop the server before managing the world.</div> 161 <div class="flash flash-warn">Stop the server before managing the world.</div>
68 {% endif %} 162 {% endif %}
69 163
70 <div class="meta" style="margin-bottom:1rem"> 164 <div class="meta" style="margin-bottom: 1rem">
71 <span><strong>Current seed:</strong> 165 <span
72 {% if let Some(seed) = current_seed %} 166 ><strong>Current seed:</strong>
73 {{ seed }} 167 {% if let Some(seed) = current_seed %} {{ seed }} {% else %}
74 {% else %} 168 <em>not yet generated</em>
75 <em>not yet generated</em> 169 {% endif %}
76 {% endif %} 170 </span>
77 </span> 171 </div>
78 </div>
79 172
80 <div class="actions" style="flex-wrap:wrap; gap:1rem; {% if instance.is_active %}pointer-events:none; opacity:0.5{% endif %}"> 173 <div
81 <a href="/instances/{{ instance.id }}/world/export" class="btn" hx-boost="false"{% if instance.is_active %} tabindex="-1"{% endif %}>Export World</a> 174 class="actions"
175 style="flex-wrap:wrap; gap:1rem; {% if instance.is_active %}pointer-events:none; opacity:0.5{% endif %}"
176 >
177 <a
178 href="/instances/{{ instance.id }}/world/export"
179 class="btn"
180 hx-boost="false"
181 {%
182 if
183 instance.is_active
184 %}
185 tabindex="-1"
186 {%
187 endif
188 %}
189 >Export World</a
190 >
82 191
83 <form action="/instances/{{ instance.id }}/world/import" method="post" enctype="multipart/form-data" style="display:flex;align-items:center;gap:0.5rem" hx-boost="false"> 192 <form
84 <input type="file" name="world_zip" accept=".zip" required style="font-size:0.85rem"{% if instance.is_active %} disabled{% endif %}> 193 action="/instances/{{ instance.id }}/world/import"
85 <button type="submit" class="btn" onclick="return confirm('This will replace the current world. Continue?')"{% if instance.is_active %} disabled{% endif %}>Import</button> 194 method="post"
86 </form> 195 enctype="multipart/form-data"
196 style="display: flex; align-items: center; gap: 0.5rem"
197 hx-boost="false"
198 >
199 <input
200 type="file"
201 name="world_zip"
202 accept=".zip"
203 required
204 style="font-size: 0.85rem"
205 {%
206 if
207 instance.is_active
208 %}
209 disabled{%
210 endif
211 %}
212 />
213 <button
214 type="submit"
215 class="btn"
216 onclick="
217 return confirm('This will replace the current world. Continue?');
218 "
219 {%
220 if
221 instance.is_active
222 %}
223 disabled{%
224 endif
225 %}
226 >
227 Import
228 </button>
229 </form>
230
231 <form
232 action="/instances/{{ instance.id }}/world/regenerate"
233 method="post"
234 class="form-row"
235 onsubmit="
236 return confirm(
237 'This will DELETE the current world and generate a new one. Continue?',
238 );
239 "
240 >
241 <input
242 type="text"
243 name="seed"
244 placeholder="Seed (empty = random)"
245 style="width: 180px"
246 {%
247 if
248 instance.is_active
249 %}
250 disabled{%
251 endif
252 %}
253 />
254 <button
255 type="submit"
256 class="btn-danger"
257 {%
258 if
259 instance.is_active
260 %}
261 disabled{%
262 endif
263 %}
264 >
265 Regenerate
266 </button>
267 </form>
268 </div>
269</div>
87 270
88 <form action="/instances/{{ instance.id }}/world/regenerate" method="post" class="form-row" onsubmit="return confirm('This will DELETE the current world and generate a new one. Continue?')"> 271<div class="card" style="margin-top: 1.5rem">
89 <input type="text" name="seed" placeholder="Seed (empty = random)" style="width:180px"{% if instance.is_active %} disabled{% endif %}> 272 <h3>Mods</h3>
90 <button type="submit" class="btn-danger"{% if instance.is_active %} disabled{% endif %}>Regenerate</button> 273 <div class="meta" style="margin-bottom: 0.5rem">
91 </form> 274 <span>
92 </div> 275 {% if instance.has_modpack %}
276 <strong>{{ mod_count }}</strong> mod{% if mod_count != 1 %}s{% endif %}
277 installed {% if rejected_count > 0 %} (<strong
278 >{{ rejected_count }}</strong
279 >
280 rejected) {% endif %} {% else %} No mods installed {% endif %}
281 </span>
282 </div>
283 <div class="actions">
284 <a href="/instances/{{ instance.id }}/mods" class="btn">Manage Mods</a>
285 </div>
93</div> 286</div>
94{% endblock %} 287{% endblock %}
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 %}