feat: add reload service command and update command handling
This commit is contained in:
36
ss/cli.py
36
ss/cli.py
@ -65,7 +65,7 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
update_parser.add_argument('--force', action='store_true', help='Force update even if binary already exists')
|
||||
|
||||
# Config commands
|
||||
config_parser = core_subparsers.add_parser('config', help='Manage core configuration')
|
||||
config_parser = subparsers.add_parser('config', help='Manage core configuration')
|
||||
config_subparsers = config_parser.add_subparsers(dest='config_command', help='Configuration operations')
|
||||
|
||||
# Import config
|
||||
@ -89,7 +89,7 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
apply_parser = config_subparsers.add_parser('apply', help='Apply active subscription to generate final config')
|
||||
|
||||
# Service management commands
|
||||
service_parser = core_subparsers.add_parser('service', help='Manage mihomo as a system service')
|
||||
service_parser = subparsers.add_parser('service', help='Manage mihomo as a system service')
|
||||
service_subparsers = service_parser.add_subparsers(dest='service_command', help='Service operations')
|
||||
|
||||
# Install service command
|
||||
@ -113,6 +113,10 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
restart_service_parser = service_subparsers.add_parser('restart', help='Restart mihomo system service')
|
||||
restart_service_parser.add_argument('--name', default='mihomo', help='Service name (default: mihomo)')
|
||||
|
||||
# Reload service command
|
||||
reload_service_parser = service_subparsers.add_parser('reload', help='Reload mihomo service configuration (via API)')
|
||||
reload_service_parser.add_argument('--name', default='mihomo', help='Service name (default: mihomo)')
|
||||
|
||||
# Status service command
|
||||
status_service_parser = service_subparsers.add_parser('status', help='Check mihomo system service status')
|
||||
status_service_parser.add_argument('--name', default='mihomo', help='Service name (default: mihomo)')
|
||||
@ -138,7 +142,7 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
def handle_subscription_command(args, subscription_manager: SubscriptionManager, core_config_manager: CoreConfigManager, parser: argparse.ArgumentParser) -> None:
|
||||
def handle_subscription_command(args, subscription_manager: SubscriptionManager, core_config_manager: CoreConfigManager, core_manager: CoreManager, parser: argparse.ArgumentParser) -> None:
|
||||
"""Handle subscription related commands."""
|
||||
if not hasattr(args, 'subcommand') or not args.subcommand:
|
||||
parser.parse_args(['subscription', '--help'])
|
||||
@ -156,7 +160,9 @@ def handle_subscription_command(args, subscription_manager: SubscriptionManager,
|
||||
subscription_manager.set_subscription_url(args.name, args.url)
|
||||
elif args.subcommand == 'activate':
|
||||
subscription_manager.activate_subscription(args.name)
|
||||
core_config_manager.apply()
|
||||
if core_config_manager.apply():
|
||||
# Reload service if config applied successfully
|
||||
core_manager.reload_service()
|
||||
elif args.subcommand == 'list':
|
||||
subscription_manager.list_subscriptions()
|
||||
elif args.subcommand == 'storage':
|
||||
@ -173,10 +179,6 @@ def handle_core_command(args, core_manager: CoreManager, core_config_manager: Co
|
||||
|
||||
if args.core_command == 'update':
|
||||
core_manager.update(version=args.version, force=args.force)
|
||||
elif args.core_command == 'config':
|
||||
handle_config_command(args, core_config_manager, parser)
|
||||
elif args.core_command == 'service':
|
||||
handle_service_command(args, core_manager, parser)
|
||||
else:
|
||||
parser.parse_args(['core', '--help'])
|
||||
|
||||
@ -184,7 +186,7 @@ def handle_core_command(args, core_manager: CoreManager, core_config_manager: Co
|
||||
def handle_config_command(args, core_config_manager: CoreConfigManager, parser: argparse.ArgumentParser) -> None:
|
||||
"""Handle configuration commands."""
|
||||
if not hasattr(args, 'config_command') or not args.config_command:
|
||||
parser.parse_args(['core', 'config', '--help'])
|
||||
parser.parse_args(['config', '--help'])
|
||||
return
|
||||
|
||||
if args.config_command == 'import':
|
||||
@ -200,13 +202,13 @@ def handle_config_command(args, core_config_manager: CoreConfigManager, parser:
|
||||
elif args.config_command == 'apply':
|
||||
core_config_manager.apply()
|
||||
else:
|
||||
parser.parse_args(['core', 'config', '--help'])
|
||||
parser.parse_args(['config', '--help'])
|
||||
|
||||
|
||||
def handle_service_command(args, core_manager: CoreManager, parser: argparse.ArgumentParser) -> None:
|
||||
"""Handle service commands."""
|
||||
if not hasattr(args, 'service_command') or not args.service_command:
|
||||
parser.parse_args(['core', 'service', '--help'])
|
||||
parser.parse_args(['service', '--help'])
|
||||
return
|
||||
|
||||
if args.service_command == 'install':
|
||||
@ -232,11 +234,15 @@ def handle_service_command(args, core_manager: CoreManager, parser: argparse.Arg
|
||||
success = core_manager.restart_service(service_name=args.name)
|
||||
if not success:
|
||||
sys.exit(1)
|
||||
elif args.service_command == 'reload':
|
||||
success = core_manager.reload_service(service_name=args.name)
|
||||
if not success:
|
||||
sys.exit(1)
|
||||
elif args.service_command == 'status':
|
||||
status = core_manager.get_service_status(service_name=args.name)
|
||||
print(f"Service '{args.name}' status: {status}")
|
||||
else:
|
||||
parser.parse_args(['core', 'service', '--help'])
|
||||
parser.parse_args(['service', '--help'])
|
||||
|
||||
|
||||
def handle_hook_command(args, hook_manager: HookManager, parser: argparse.ArgumentParser) -> None:
|
||||
@ -274,9 +280,13 @@ def main() -> None:
|
||||
|
||||
try:
|
||||
if args.command == 'subscription':
|
||||
handle_subscription_command(args, subscription_manager, core_config_manager, parser)
|
||||
handle_subscription_command(args, subscription_manager, core_config_manager, core_manager, parser)
|
||||
elif args.command == 'core':
|
||||
handle_core_command(args, core_manager, core_config_manager, parser)
|
||||
elif args.command == 'config':
|
||||
handle_config_command(args, core_config_manager, parser)
|
||||
elif args.command == 'service':
|
||||
handle_service_command(args, core_manager, parser)
|
||||
elif args.command == 'hook':
|
||||
handle_hook_command(args, hook_manager, parser)
|
||||
else:
|
||||
|
||||
@ -433,6 +433,73 @@ class CoreManager:
|
||||
except Exception as e:
|
||||
return f"Error checking service status: {e}"
|
||||
|
||||
def reload_service(self, service_name: str = "mihomo") -> bool:
|
||||
"""
|
||||
Reload mihomo configuration via external controller API without restarting service.
|
||||
|
||||
Args:
|
||||
service_name: Name of the service (default: "mihomo")
|
||||
|
||||
Returns:
|
||||
bool: True if reload successful, False otherwise.
|
||||
"""
|
||||
config_path = self.core_config_manager.storage.config_dir / "generated_config.yaml"
|
||||
if not config_path.exists():
|
||||
print(f"❌ Configuration file not found: {config_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Parse generated config to find external-controller
|
||||
import yaml
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
external_controller = config.get('external-controller', '127.0.0.1:9090')
|
||||
secret = config.get('secret', '')
|
||||
|
||||
# Format API URL
|
||||
if not external_controller.startswith('http'):
|
||||
base_url = f"http://{external_controller}"
|
||||
else:
|
||||
base_url = external_controller
|
||||
|
||||
# Prepare request
|
||||
url = f"{base_url}/configs?force=true"
|
||||
headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if secret:
|
||||
headers['Authorization'] = f"Bearer {secret}"
|
||||
|
||||
payload = {
|
||||
"path": str(config_path.absolute()),
|
||||
"payload": "" # Empty payload suggests reload from path
|
||||
}
|
||||
|
||||
# Send reload request
|
||||
print(f"🔄 Reloading configuration via API: {url}")
|
||||
# Short timeout as local controller should respond quickly
|
||||
response = requests.put(url, json=payload, headers=headers, timeout=2)
|
||||
|
||||
if response.status_code == 204:
|
||||
print(f"✅ Configuration reloaded successfully")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Failed to reload configuration: HTTP {response.status_code}")
|
||||
print(f" Response: {response.text}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"❌ Failed to connect to external controller: {e}")
|
||||
print(" Is the service running?")
|
||||
return False
|
||||
except ImportError:
|
||||
print("❌ PyYAML is required to parse configuration.")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Error reloading service: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def deep_merge(dict1, dict2):
|
||||
for k, v in dict2.items():
|
||||
|
||||
Reference in New Issue
Block a user