import importlib.util import sys from pathlib import Path from flask import Flask, render_template_string from werkzeug.middleware.dispatcher import DispatcherMiddleware from werkzeug.serving import run_simple root = Flask(__name__) @root.route("/") def index(): apps = [ d.name for d in Path(__file__).parent.iterdir() if d.is_dir() and (d / "app.py").exists() ] links = "".join(f'
  • {a}
  • ' for a in sorted(apps)) return render_template_string(f""" Tools

    Tools

    """) def load_sub_apps(): mounts = {} base = Path(__file__).parent for app_dir in sorted(base.iterdir()): app_file = app_dir / "app.py" if not app_dir.is_dir() or not app_file.exists(): continue spec = importlib.util.spec_from_file_location(app_dir.name, app_file) mod = importlib.util.module_from_spec(spec) sys.modules[app_dir.name] = mod # must be registered before exec so Flask(__name__) resolves root_path correctly spec.loader.exec_module(mod) mounts[f"/{app_dir.name}"] = mod.app print(f" mounted /{app_dir.name}") return mounts application = DispatcherMiddleware(root, load_sub_apps()) if __name__ == "__main__": run_simple("0.0.0.0", 8080, application, use_reloader=False)