This commit is contained in:
2026-01-14 01:33:25 -08:00
parent 226367a6ea
commit e27e13b64c
10 changed files with 760 additions and 63 deletions

110
CLI.py
View File

@@ -4415,6 +4415,116 @@ class MedeiaCLI:
def repl() -> None:
self.run_repl()
@app.command("remote-server")
def remote_server(
storage_path: str = typer.Argument(
None, help="Path to the store folder or store name from config"
),
port: int = typer.Option(None, "--port", help="Port to run the server on"),
api_key: str | None = typer.Option(None, "--api-key", help="API key for authentication"),
host: str = "0.0.0.0",
debug_server: bool = False,
background: bool = False,
) -> None:
"""Start the remote storage Flask server.
If no path is provided, it looks for [networking=zerotier] 'serve' and 'port' in config.
'serve' can be a path or the name of a [store=folder] entry.
Examples:
mm remote-server C:\\path\\to\\store --port 999 --api-key mykey
mm remote-server my_folder_name
mm remote-server --background
"""
try:
from scripts import remote_storage_server as rss
except Exception as exc:
print(
"Error: remote_storage_server script not available:",
exc,
file=sys.stderr,
)
return
# Ensure Flask present
if not getattr(rss, "HAS_FLASK", False):
print(
"ERROR: Flask and flask-cors required; install with: pip install flask flask-cors",
file=sys.stderr,
)
return
from SYS.config import load_config
conf = load_config()
# Resolve from Networking config if omitted
zt_conf = conf.get("networking", {}).get("zerotier", {})
if not storage_path:
storage_path = zt_conf.get("serve")
if port is None:
port = int(zt_conf.get("port") or 999)
if api_key is None:
api_key = zt_conf.get("api_key")
if not storage_path:
print(
"Error: No storage path provided and no [networking=zerotier] 'serve' configured.",
file=sys.stderr,
)
return
from pathlib import Path
# Check if storage_path is a named folder store
folders = conf.get("store", {}).get("folder", {})
found_path = None
for name, block in folders.items():
if name.lower() == storage_path.lower():
found_path = block.get("path") or block.get("PATH")
break
if found_path:
storage = Path(found_path).resolve()
else:
storage = Path(storage_path).resolve()
if not storage.exists():
print(f"Error: Storage path does not exist: {storage}", file=sys.stderr)
return
rss.STORAGE_PATH = storage
rss.API_KEY = api_key
try:
app_obj = rss.create_app()
except Exception as exc:
print("Failed to create remote_storage_server app:", exc, file=sys.stderr)
return
print(
f"Starting remote storage server at http://{host}:{port}, storage: {storage}"
)
if background:
try:
from werkzeug.serving import make_server
import threading
server = make_server(host, port, app_obj)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
print(f"Server started in background (thread id={thread.ident})")
return
except Exception as exc:
print("Failed to start background server, falling back to foreground:", exc, file=sys.stderr)
# Foreground run blocks the CLI until server exits
try:
app_obj.run(host=host, port=port, debug=debug_server, use_reloader=False, threaded=True)
except KeyboardInterrupt:
print("Remote server stopped by user")
@app.callback(invoke_without_command=True)
def main_callback(ctx: typer.Context) -> None:
if ctx.invoked_subcommand is None: