import os import sys import threading import subprocess from http.server import BaseHTTPRequestHandler, HTTPServer import mimetypes from functools import lru_cache from jsmin import jsmin # pip install jsmin import time from log.Logger import * logger = Logger() import PyPost PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) HTML_DIR = os.path.join(PROJECT_ROOT, "html") MARKDOWN_DIR = os.path.join(PROJECT_ROOT, "markdown") BASE_FILE = os.path.join(HTML_DIR, "base", "index.html") def get_html_files(directory=HTML_DIR): html_files = [] for entry in os.listdir(directory): full_path = os.path.join(directory, entry) if os.path.isfile(full_path) and entry.endswith(".html"): html_files.append(entry) return html_files def get_markdown_files(): """Get list of .md files from the markdown directory.""" if not os.path.exists(MARKDOWN_DIR): return [] markdown_files = [] for entry in os.listdir(MARKDOWN_DIR): full_path = os.path.join(MARKDOWN_DIR, entry) if os.path.isfile(full_path) and entry.endswith(".md"): markdown_files.append(entry) return markdown_files def build_index_page(): with open(BASE_FILE, "r", encoding="utf-8") as f: base_html = f.read() html_files = get_html_files(HTML_DIR) links = "\n".join(f'
  • {fname}
  • ' for fname in html_files) content = f"" # Insert footer after content full_content = content + index_footer() return base_html.replace("", full_content) import base64 import random from hashes.hashes import hash_list @lru_cache def index_footer(): h1 = random.choice(hash_list) # Ensure h2 is different from h1 h2_candidates = [h for h in hash_list if h != h1] h2 = random.choice(h2_candidates) if h2_candidates else h1 return f""" """ class MyHandler(BaseHTTPRequestHandler): def do_GET(self): req_path = self.path.lstrip("/") # Handle root/index if req_path == "" or req_path == "index.html": content = build_index_page() self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(content.encode("utf-8")) return # Handle markdown file downloads if req_path.startswith("markdown/"): markdown_filename = req_path[9:] # Remove "markdown/" prefix # Security check: only allow .md files and prevent directory traversal if not markdown_filename.endswith(".md") or ".." in markdown_filename or "/" in markdown_filename: self.send_response(403) self.end_headers() self.wfile.write(b"403 - Forbidden: Only .md files allowed") return markdown_file_path = os.path.join(MARKDOWN_DIR, markdown_filename) # Check if file exists and is within markdown directory if not os.path.exists(markdown_file_path) or not os.path.isfile(markdown_file_path): self.send_response(404) self.end_headers() self.wfile.write(b"404 - Markdown file not found") return # Verify the resolved path is still within the markdown directory (extra security) resolved_path = os.path.realpath(markdown_file_path) resolved_markdown_dir = os.path.realpath(MARKDOWN_DIR) if not resolved_path.startswith(resolved_markdown_dir): self.send_response(403) self.end_headers() self.wfile.write(b"403 - Forbidden") return try: with open(markdown_file_path, "rb") as f: content = f.read() self.send_response(200) self.send_header("Content-type", "text/markdown") self.send_header("Content-Disposition", f'attachment; filename="{markdown_filename}"') self.end_headers() self.wfile.write(content) logger.log_info(f"Served markdown file: {markdown_filename}") return except Exception as e: logger.log_error(f"Error serving markdown file {markdown_filename}: {e}") self.send_response(500) self.end_headers() self.wfile.write(b"500 - Internal Server Error") return # Handle other files (existing functionality) file_path = os.path.normpath(os.path.join(PROJECT_ROOT, req_path)) if not file_path.startswith(PROJECT_ROOT): self.send_response(403) self.end_headers() self.wfile.write(b"403 - Forbidden") return if os.path.isfile(file_path): mime_type, _ = mimetypes.guess_type(file_path) if mime_type is None: mime_type = "application/octet-stream" with open(file_path, "rb") as f: content = f.read() # Obfuscate JS on the fly if mime_type == "application/javascript" or file_path.endswith(".js"): try: content = jsmin(content.decode("utf-8")).encode("utf-8") except Exception as e: logger.log_error(f"Error minifying JS file {file_path}: {e}") self.send_response(200) self.send_header("Content-type", mime_type) self.end_headers() self.wfile.write(content) return self.send_response(404) self.end_headers() self.wfile.write(b"404 - Not Found") def run_pypost(): """Run PyPost.py in a separate process.""" script = os.path.join(PROJECT_ROOT, "PyPost.py") subprocess.run([sys.executable, script]) if __name__ == "__main__": try: threading.Thread(target=run_pypost, daemon=True).start() logger.log_debug("Started PyPost.py in background watcher thread.") server_address = ("localhost", 8000) httpd = HTTPServer(server_address, MyHandler) logger.log_info(f"Serving on http://{server_address[0]}:{server_address[1]}") httpd.serve_forever() except (Exception, KeyboardInterrupt) as e: logger.log_info(f"Shutting down server.\n Reason: {e}") httpd.server_close()