93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
import http.server
|
|
import os
|
|
import sys
|
|
import urllib.parse
|
|
|
|
directory = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else ".")
|
|
mods_root = os.path.join(directory, "mods")
|
|
|
|
|
|
def _path_under_mods(rel: str) -> str | None:
|
|
"""Map URL path segment after /mods to a path only inside ``mods_root``. Returns None if unsafe."""
|
|
full = os.path.realpath(os.path.join(mods_root, rel))
|
|
root = os.path.realpath(mods_root)
|
|
if full == root or full.startswith(root + os.sep):
|
|
return full
|
|
return None
|
|
|
|
HELLO_PAGE = b"""\
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Modpack Install</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: system-ui, sans-serif; background: #111; color: #ddd;
|
|
max-width: 640px; margin: 40px auto; padding: 0 20px; line-height: 1.6; }
|
|
h1 { color: #fff; margin-bottom: 1rem; font-size: 1.4rem; }
|
|
h2 { color: #aaa; font-size: .9rem; font-weight: 400; margin: 1.2rem 0 .4rem; }
|
|
ol { padding-left: 1.2rem; }
|
|
li { margin-bottom: .5rem; }
|
|
code { background: #222; padding: 2px 6px; border-radius: 4px; font-size: .9em; }
|
|
pre { background: #222; padding: 10px 14px; border-radius: 6px; overflow-x: auto;
|
|
margin: .4rem 0; font-size: .85em; }
|
|
a { color: #7af; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Modpack Setup</h1>
|
|
<ol>
|
|
<li>Install <a href="https://prismlauncher.org/download/">Prism Launcher</a></li>
|
|
<p>If on Arch:</p>
|
|
<pre>sudo pacman -S prism-launcher</pre>
|
|
<li>Create a <strong>1.20.1 Forge 47.4.10</strong> instance, launch it once, then close</li>
|
|
<li>Download mods:
|
|
<pre>wget -rnH -R "index.html*" http://barrys.cloud/mods/</pre>
|
|
</li>
|
|
<li>In Prism, right-click the instance → <strong>Folder</strong> → replace the <code>mods/</code> dir with the downloaded one</li>
|
|
<li>Launch & connect to <code>barrys.cloud</code></li>
|
|
</ol>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
class Handler(http.server.SimpleHTTPRequestHandler):
|
|
def translate_path(self, path):
|
|
path = urllib.parse.unquote(urllib.parse.urlparse(path).path)
|
|
if path == "/mods" or path.startswith("/mods/"):
|
|
rel = path[len("/mods"):].lstrip("/")
|
|
mapped = _path_under_mods(rel)
|
|
return mapped if mapped is not None else os.path.join(mods_root, "__invalid__")
|
|
return os.path.join(mods_root, "__invalid__")
|
|
|
|
def do_GET(self):
|
|
if self.path == "/":
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "text/html")
|
|
self.send_header("Content-Length", len(HELLO_PAGE))
|
|
self.end_headers()
|
|
self.wfile.write(HELLO_PAGE)
|
|
return
|
|
if not self.path.startswith("/mods"):
|
|
self.send_error(404)
|
|
return
|
|
super().do_GET()
|
|
|
|
|
|
|
|
def main() -> None:
|
|
port = 80
|
|
try:
|
|
http.server.HTTPServer(("0.0.0.0", port), Handler).serve_forever()
|
|
except OSError as e:
|
|
print(f"bind failed on 0.0.0.0:{port}: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|