A Python port of the Invisible Internet Project (I2P)
at main 106 lines 3.7 kB view raw
1"""CLI entry point for the I2P router.""" 2 3import argparse 4import asyncio 5import logging 6import os 7import signal 8import sys 9 10from i2p_router.bootstrap import RouterBootstrap 11from i2p_router.config import RouterConfig 12 13 14def main() -> None: 15 parser = argparse.ArgumentParser(description="I2P Python Router") 16 parser.add_argument("--host", default="0.0.0.0", help="Listen host (default: 0.0.0.0)") 17 parser.add_argument("--port", type=int, default=9700, help="NTCP2 listen port (default: 9700)") 18 parser.add_argument("--health-port", type=int, default=9701, help="Health endpoint port (default: 9701)") 19 parser.add_argument("--data-dir", default="~/.i2p-python", help="Data directory (default: ~/.i2p-python)") 20 parser.add_argument("--reseed", action="store_true", help="Force reseed on start") 21 parser.add_argument( 22 "--setup", 23 nargs="?", 24 const="auto", 25 metavar="MODE", 26 help="Run first-run setup wizard (modes: auto, paranoid, performance, interactive)", 27 ) 28 parser.add_argument( 29 "--log-level", 30 default="INFO", 31 choices=["DEBUG", "INFO", "WARNING", "ERROR"], 32 help="Log level (default: INFO)", 33 ) 34 args = parser.parse_args() 35 36 # Console logging 37 log_level = getattr(logging, args.log_level) 38 log_format = "%(asctime)s %(levelname)-8s %(name)s: %(message)s" 39 root_logger = logging.getLogger() 40 root_logger.setLevel(log_level) 41 42 console_handler = logging.StreamHandler(sys.stdout) 43 console_handler.setFormatter(logging.Formatter(log_format)) 44 root_logger.addHandler(console_handler) 45 46 # File logging (always DEBUG for post-mortem analysis) 47 data_dir = os.path.expanduser(args.data_dir) 48 os.makedirs(data_dir, exist_ok=True) 49 log_path = os.path.join(data_dir, "router.log") 50 file_handler = logging.FileHandler(log_path) 51 file_handler.setLevel(logging.DEBUG) 52 file_handler.setFormatter(logging.Formatter(log_format)) 53 root_logger.addHandler(file_handler) 54 55 log = logging.getLogger(__name__) 56 57 # Run setup wizard if --setup or first run 58 from i2p_apps.setup.wizard import SetupWizard 59 wizard = SetupWizard(data_dir=data_dir) 60 if args.setup: 61 mode = args.setup 62 if mode == "interactive": 63 wizard.run_interactive() 64 else: 65 wizard.run_auto(mode=mode) 66 log.info("Setup complete, config saved to %s", data_dir) 67 elif wizard.is_first_run(): 68 log.info("First run detected — running auto setup") 69 wizard.run_auto(mode="normal") 70 71 log.info("I2P Python Router starting") 72 log.info(" NTCP2 port: %d", args.port) 73 log.info(" Health port: %d", args.health_port) 74 log.info(" Data dir: %s", args.data_dir) 75 log.info(" Log file: %s", log_path) 76 log.info(" Log level: %s (file always DEBUG)", args.log_level) 77 78 config = RouterConfig( 79 listen_host=args.host, 80 listen_port=args.port, 81 health_port=args.health_port, 82 data_dir=args.data_dir, 83 ) 84 config = RouterConfig.with_env_overrides(config) 85 86 bootstrap = RouterBootstrap(config) 87 88 async def run() -> None: 89 await bootstrap.start() 90 log.info( 91 "Router running — NTCP2 on %s:%d, health on %s:%d", 92 args.host, args.port, args.host, args.health_port, 93 ) 94 stop = asyncio.Event() 95 loop = asyncio.get_event_loop() 96 for sig in (signal.SIGTERM, signal.SIGINT): 97 loop.add_signal_handler(sig, stop.set) 98 await stop.wait() 99 log.info("Signal received, shutting down...") 100 await bootstrap.shutdown() 101 102 asyncio.run(run()) 103 104 105if __name__ == "__main__": 106 main()