feat: переход с использования connection.py на outbound.py
This commit is contained in:
@@ -1,16 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
from dataclasses import dataclass
|
||||||
import secrets
|
|
||||||
import uuid
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from collections import defaultdict
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, TypeVar
|
from typing import TYPE_CHECKING
|
||||||
from urllib.parse import parse_qs, quote, unquote, urlencode, urlparse
|
|
||||||
|
|
||||||
from .connection import ConnectionFactory
|
from .outbound import OutboundFactory
|
||||||
|
from .target import TargetStorage
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -23,7 +18,7 @@ class Profile:
|
|||||||
user: User
|
user: User
|
||||||
|
|
||||||
def to_text(self):
|
def to_text(self):
|
||||||
links = [connection.to_link() for connection in self.user.connections]
|
links = [outbound.to_link() for outbound in self.user.outbounds]
|
||||||
text = "\n".join(links)
|
text = "\n".join(links)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
@@ -94,12 +89,16 @@ class ProfileStorage:
|
|||||||
if line.strip()
|
if line.strip()
|
||||||
]
|
]
|
||||||
|
|
||||||
connections = [ConnectionFactory.from_link(link) for link in links]
|
target_storage = TargetStorage(self.xrmc)
|
||||||
user = User(username, connections)
|
|
||||||
|
outbounds = [
|
||||||
|
OutboundFactory.from_link(target_storage, link) for link in links
|
||||||
|
]
|
||||||
|
user = User(username, outbounds)
|
||||||
|
|
||||||
profile = Profile(folder_name=path.parent.name, user=user)
|
profile = Profile(folder_name=path.parent.name, user=user)
|
||||||
print(
|
print(
|
||||||
f"[DEBUG] Loaded profile: folder='{profile.folder_name}', connections={len(connections)}"
|
f"[DEBUG] Loaded profile: folder='{profile.folder_name}', outbounds={len(outbounds)}"
|
||||||
)
|
)
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
|||||||
@@ -3,51 +3,62 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from .connection import ConnectionFactory, ConnectionKey
|
from .outbound import Outbound, OutboundFactory, OutboundSpec
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .connection import Connection
|
from .target import TargetStorage
|
||||||
from .xray_config import XrayConfig
|
from .xray_config import XrayConfig
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class User:
|
class User:
|
||||||
username: str
|
username: str
|
||||||
connections: list[Connection] = field(default_factory=list)
|
outbounds: list[Outbound] = field(default_factory=list)
|
||||||
|
|
||||||
def modify_connection_by_key(
|
def modify_outbounds(
|
||||||
self,
|
self,
|
||||||
xray_config: "XrayConfig",
|
xray_config: XrayConfig,
|
||||||
add: list[ConnectionKey] = [],
|
target_storage: TargetStorage,
|
||||||
delete: list[ConnectionKey] = [],
|
to_add: list[OutboundSpec] | None = None,
|
||||||
|
to_delete: list[OutboundSpec] | None = None,
|
||||||
):
|
):
|
||||||
key_map = {
|
to_add = to_add or []
|
||||||
ConnectionKey(c.protocol, c.exit_point): c for c in self.connections
|
to_delete = to_delete or []
|
||||||
|
|
||||||
|
user_specs = {
|
||||||
|
OutboundSpec(o.protocol, o.target.id): o for o in self.outbounds
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in delete:
|
for spec in to_delete:
|
||||||
key_map.pop(key, None)
|
user_specs.pop(spec, None)
|
||||||
|
|
||||||
for key in add:
|
for spec in to_add:
|
||||||
if key not in key_map:
|
if spec not in user_specs:
|
||||||
connection = ConnectionFactory.from_spec(key, xray_config)
|
outbound = OutboundFactory.from_spec(
|
||||||
key_map[key] = connection
|
xray_config, target_storage, spec
|
||||||
|
)
|
||||||
|
user_specs[spec] = outbound
|
||||||
|
|
||||||
self.connections = list(key_map.values())
|
self.outbounds = list(user_specs.values())
|
||||||
|
|
||||||
|
|
||||||
class UserFactory:
|
class UserFactory:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_spec(
|
def from_spec(
|
||||||
username: str, keys: list[ConnectionKey], xray_config: "XrayConfig"
|
username: str,
|
||||||
|
specs: list[OutboundSpec],
|
||||||
|
xray_config: XrayConfig,
|
||||||
|
target_storage: TargetStorage,
|
||||||
) -> User:
|
) -> User:
|
||||||
connections = []
|
outbounds = []
|
||||||
|
|
||||||
for key in keys:
|
for spec in specs:
|
||||||
conn = ConnectionFactory.from_spec(key, xray_config)
|
outbound = OutboundFactory.from_spec(
|
||||||
connections.append(conn)
|
xray_config, target_storage, spec
|
||||||
|
)
|
||||||
|
outbounds.append(outbound)
|
||||||
|
|
||||||
user = User(username=username, connections=connections)
|
user = User(username=username, outbounds=outbounds)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -5,17 +5,21 @@ from collections import defaultdict
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from .connection import ConnectionFactory
|
from .outbound import OutboundFactory
|
||||||
|
from .target import TargetStorage
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .connection import Connection
|
from .outbound import Outbound
|
||||||
|
|
||||||
|
|
||||||
class XrayConfig:
|
class XrayConfig:
|
||||||
def __init__(self, config_folder: Path, host: str):
|
def __init__(
|
||||||
|
self, config_folder: Path, host: str, target_storage: TargetStorage
|
||||||
|
):
|
||||||
self.path = config_folder
|
self.path = config_folder
|
||||||
self.host = host
|
self.host = host
|
||||||
|
self.ts = target_storage
|
||||||
|
|
||||||
self.inbounds_files = list(self.path.glob("*-in-*.json"))
|
self.inbounds_files = list(self.path.glob("*-in-*.json"))
|
||||||
self.outbounds_files = list(self.path.glob("*-out-*.json"))
|
self.outbounds_files = list(self.path.glob("*-out-*.json"))
|
||||||
@@ -37,14 +41,14 @@ class XrayConfig:
|
|||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _find_inbound(self, connection: Connection) -> tuple[str, dict]:
|
def _find_inbound(self, outbound: Outbound) -> tuple[str, dict]:
|
||||||
for filename, data in self.inbounds_data.items():
|
for filename, data in self.inbounds_data.items():
|
||||||
inbounds = data.get("inbounds", [])
|
inbounds = data.get("inbounds", [])
|
||||||
for inbound in inbounds:
|
for inbound in inbounds:
|
||||||
if connection.matches_inbound(inbound):
|
if outbound.matches_inbound(inbound):
|
||||||
return filename, inbound
|
return filename, inbound
|
||||||
|
|
||||||
raise RuntimeError(f"Inbound not found for {connection}")
|
raise RuntimeError(f"Inbound not found for {outbound}")
|
||||||
|
|
||||||
def find_managed_inbound_by_protocol(self, protocol: str) -> dict:
|
def find_managed_inbound_by_protocol(self, protocol: str) -> dict:
|
||||||
for data in self.inbounds_data.values():
|
for data in self.inbounds_data.values():
|
||||||
@@ -62,40 +66,27 @@ class XrayConfig:
|
|||||||
f"Managed inbound not found for protocol: {protocol}"
|
f"Managed inbound not found for protocol: {protocol}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Устаревший метод. Перейти на использование xray_config.get_targets_list()
|
|
||||||
def get_exit_points(self) -> set[str]:
|
|
||||||
exit_points = set()
|
|
||||||
for data in self.outbounds_data.values():
|
|
||||||
outbounds = data.get("outbounds", [])
|
|
||||||
for outbound in outbounds:
|
|
||||||
tag: str = outbound.get("tag")
|
|
||||||
if tag:
|
|
||||||
exit_point = tag.rsplit("-", 1)[1]
|
|
||||||
exit_points.add(exit_point)
|
|
||||||
exit_points.add("default")
|
|
||||||
return exit_points
|
|
||||||
|
|
||||||
def get_users(self) -> list[User]:
|
def get_users(self) -> list[User]:
|
||||||
connection_map: defaultdict[str, list[Connection]] = defaultdict(list)
|
outbound_map: defaultdict[str, list[Outbound]] = defaultdict(list)
|
||||||
|
|
||||||
for data in self.inbounds_data.values():
|
for data in self.inbounds_data.values():
|
||||||
inbounds = data.get("inbounds", [])
|
inbounds = data.get("inbounds", [])
|
||||||
for inbound in inbounds:
|
for inbound in inbounds:
|
||||||
protocol = inbound.get("protocol")
|
protocol = inbound.get("protocol")
|
||||||
|
|
||||||
if protocol in ConnectionFactory._registry:
|
if protocol in OutboundFactory._registry:
|
||||||
settings = inbound.get("settings", {})
|
settings = inbound.get("settings", {})
|
||||||
clients = settings.get("clients", [])
|
clients = settings.get("clients", [])
|
||||||
for client in clients:
|
for client in clients:
|
||||||
username, connection = ConnectionFactory.from_inbound(
|
username, outbound = OutboundFactory.from_inbound(
|
||||||
self.host, client, inbound
|
self.ts, self.host, client, inbound
|
||||||
)
|
)
|
||||||
connection_map[username].append(connection)
|
outbound_map[username].append(outbound)
|
||||||
|
|
||||||
users = []
|
users = []
|
||||||
|
|
||||||
for username, connections in connection_map.items():
|
for username, outbounds in outbound_map.items():
|
||||||
users.append(User(username, connections))
|
users.append(User(username, outbounds))
|
||||||
|
|
||||||
return users
|
return users
|
||||||
|
|
||||||
@@ -108,30 +99,26 @@ class XrayConfig:
|
|||||||
def add_user(self, user: User) -> None:
|
def add_user(self, user: User) -> None:
|
||||||
modified_files = set()
|
modified_files = set()
|
||||||
|
|
||||||
for connection in user.connections:
|
for outbound in user.outbounds:
|
||||||
filename, inbound = self._find_inbound(connection)
|
filename, inbound = self._find_inbound(outbound)
|
||||||
connection.add_to_inbound(inbound, user.username)
|
outbound.add_to_inbound(inbound, user.username)
|
||||||
modified_files.add(filename)
|
modified_files.add(filename)
|
||||||
|
|
||||||
for filename in modified_files:
|
for filename in modified_files:
|
||||||
path = self.path / filename
|
path = self.path / filename
|
||||||
self._save_json(self.inbounds_data[filename], path)
|
self._save_json(self.inbounds_data[filename], path)
|
||||||
|
|
||||||
def delete_user(self, username: str) -> bool:
|
def delete_user(self, user: User) -> bool:
|
||||||
user = self.get_user(username)
|
|
||||||
if not user:
|
|
||||||
return False
|
|
||||||
|
|
||||||
deleted = False
|
deleted = False
|
||||||
modified_files = set()
|
modified_files = set()
|
||||||
|
|
||||||
for connection in user.connections:
|
for outbound in user.outbounds:
|
||||||
try:
|
try:
|
||||||
filename, inbound = self._find_inbound(connection)
|
filename, inbound = self._find_inbound(outbound)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
connection.delete_from_inbound(inbound, username)
|
outbound.delete_from_inbound(inbound, user.username)
|
||||||
modified_files.add(filename)
|
modified_files.add(filename)
|
||||||
deleted = True
|
deleted = True
|
||||||
|
|
||||||
@@ -147,22 +134,22 @@ class XrayConfig:
|
|||||||
if current_user is None:
|
if current_user is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
old_connections = set(current_user.connections)
|
old_outbounds = set(current_user.outbounds)
|
||||||
new_connections = set(user.connections)
|
new_outbounds = set(user.outbounds)
|
||||||
|
|
||||||
to_add = new_connections - old_connections
|
to_add = new_outbounds - old_outbounds
|
||||||
to_remove = old_connections - new_connections
|
to_remove = old_outbounds - new_outbounds
|
||||||
|
|
||||||
modified_files = set()
|
modified_files = set()
|
||||||
|
|
||||||
for connection in to_remove:
|
for outbound in to_remove:
|
||||||
filename, inbound = self._find_inbound(connection)
|
filename, inbound = self._find_inbound(outbound)
|
||||||
connection.delete_from_inbound(inbound, user.username)
|
outbound.delete_from_inbound(inbound, user.username)
|
||||||
modified_files.add(filename)
|
modified_files.add(filename)
|
||||||
|
|
||||||
for connection in to_add:
|
for outbound in to_add:
|
||||||
filename, inbound = self._find_inbound(connection)
|
filename, inbound = self._find_inbound(outbound)
|
||||||
connection.add_to_inbound(inbound, user.username)
|
outbound.add_to_inbound(inbound, user.username)
|
||||||
modified_files.add(filename)
|
modified_files.add(filename)
|
||||||
|
|
||||||
for filename in modified_files:
|
for filename in modified_files:
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from .core import (
|
from .core import (
|
||||||
ConnectionFactory,
|
OutboundFactory,
|
||||||
ConnectionKey,
|
OutboundSpec,
|
||||||
Profile,
|
Profile,
|
||||||
ProfileFactory,
|
ProfileFactory,
|
||||||
ProfileStorage,
|
ProfileStorage,
|
||||||
|
TargetStorage,
|
||||||
User,
|
User,
|
||||||
UserFactory,
|
UserFactory,
|
||||||
XrayConfig,
|
XrayConfig,
|
||||||
@@ -34,11 +35,15 @@ def _resolve_config_path() -> str:
|
|||||||
|
|
||||||
xray_manager_config = XrayManagerConfig(_resolve_config_path())
|
xray_manager_config = XrayManagerConfig(_resolve_config_path())
|
||||||
|
|
||||||
xray_config = XrayConfig(
|
profile_storage = ProfileStorage(xray_manager_config)
|
||||||
xray_manager_config.xray_config_folder, xray_manager_config.host
|
|
||||||
)
|
|
||||||
|
|
||||||
storage = ProfileStorage(xray_manager_config)
|
target_storage = TargetStorage(xray_manager_config)
|
||||||
|
|
||||||
|
xray_config = XrayConfig(
|
||||||
|
xray_manager_config.xray_config_folder,
|
||||||
|
xray_manager_config.host,
|
||||||
|
target_storage,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_users(username: str | None, all: bool) -> list[User]:
|
def _resolve_users(username: str | None, all: bool) -> list[User]:
|
||||||
@@ -54,35 +59,37 @@ def _resolve_users(username: str | None, all: bool) -> list[User]:
|
|||||||
|
|
||||||
def _resolve_profiles(username: str | None, all: bool) -> list[Profile]:
|
def _resolve_profiles(username: str | None, all: bool) -> list[Profile]:
|
||||||
if all:
|
if all:
|
||||||
return storage.load_profiles()
|
return profile_storage.load_profiles()
|
||||||
if username:
|
if username:
|
||||||
try:
|
try:
|
||||||
profile = storage.load_profile(username)
|
profile = profile_storage.load_profile(username)
|
||||||
except (ValueError, FileNotFoundError) as e:
|
except (ValueError, FileNotFoundError) as e:
|
||||||
raise ValueError(f"Cannot load profile for {username}: {e}")
|
raise ValueError(f"Cannot load profile for {username}: {e}")
|
||||||
return [profile]
|
return [profile]
|
||||||
raise ValueError("Either username or --all must be specified")
|
raise ValueError("Either username or --all must be specified")
|
||||||
|
|
||||||
|
|
||||||
def _parse_connection_spec(spec: str) -> ConnectionKey:
|
def _parse_outbound_spec(spec: str) -> OutboundSpec:
|
||||||
protocol, exit_point = spec.split(":", 1)
|
protocol, target_id = spec.split(":", 1)
|
||||||
if protocol not in ConnectionFactory.get_protocols():
|
if protocol not in OutboundFactory.get_protocols():
|
||||||
raise ValueError(f"Unsupported protocol: {protocol}")
|
raise ValueError(f"Unsupported protocol: {protocol}")
|
||||||
if exit_point not in xray_config.get_exit_points():
|
if target_id not in {
|
||||||
raise ValueError(f"Unsupported exit point: {exit_point}")
|
target.id for target in target_storage.load_all_targets()
|
||||||
return ConnectionKey(protocol, exit_point)
|
}:
|
||||||
|
raise ValueError(f"Target ID not found: {target_id}")
|
||||||
|
return OutboundSpec(protocol, target_id)
|
||||||
|
|
||||||
|
|
||||||
def _build_user_rows(users: list[User]) -> list[list[str]]:
|
def _build_user_rows(users: list[User]) -> list[list[str]]:
|
||||||
rows = []
|
rows = []
|
||||||
for index, user in enumerate(users, start=1):
|
for index, user in enumerate(users, start=1):
|
||||||
first = True
|
first = True
|
||||||
for conn in user.connections:
|
for out in user.outbounds:
|
||||||
if first:
|
if first:
|
||||||
row = [index, user.username, conn.exit_point, conn.protocol]
|
row = [index, user.username, out.target.id, out.protocol]
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
row = ["", "", conn.exit_point, conn.protocol]
|
row = ["", "", out.target.id, out.protocol]
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
@@ -91,18 +98,18 @@ def _build_profile_rows(profiles: list[Profile]) -> list[list[str]]:
|
|||||||
rows = []
|
rows = []
|
||||||
for index, profile in enumerate(profiles, start=1):
|
for index, profile in enumerate(profiles, start=1):
|
||||||
first = True
|
first = True
|
||||||
for conn in profile.user.connections:
|
for out in profile.user.outbounds:
|
||||||
if first:
|
if first:
|
||||||
row = [
|
row = [
|
||||||
index,
|
index,
|
||||||
profile.user.username,
|
profile.user.username,
|
||||||
profile.folder_name,
|
profile.folder_name,
|
||||||
conn.exit_point,
|
out.target.id,
|
||||||
conn.protocol,
|
out.protocol,
|
||||||
]
|
]
|
||||||
first = False
|
first = False
|
||||||
else:
|
else:
|
||||||
row = ["", "", "", conn.exit_point, conn.protocol]
|
row = ["", "", "", out.target.id, out.protocol]
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
@@ -113,12 +120,15 @@ def user_add(username: str, connection: list[str], **_):
|
|||||||
if isinstance(user, User):
|
if isinstance(user, User):
|
||||||
raise ValueError(f"User already exists: {username}")
|
raise ValueError(f"User already exists: {username}")
|
||||||
|
|
||||||
keys: list[ConnectionKey] = [
|
specs: list[OutboundSpec] = [
|
||||||
_parse_connection_spec(spec) for spec in connection
|
_parse_outbound_spec(raw_spec) for raw_spec in connection
|
||||||
]
|
]
|
||||||
|
|
||||||
user = UserFactory.from_spec(
|
user = UserFactory.from_spec(
|
||||||
username=username, keys=keys, xray_config=xray_config
|
username=username,
|
||||||
|
specs=specs,
|
||||||
|
xray_config=xray_config,
|
||||||
|
target_storage=target_storage,
|
||||||
)
|
)
|
||||||
|
|
||||||
xray_config.add_user(user)
|
xray_config.add_user(user)
|
||||||
@@ -127,7 +137,7 @@ def user_add(username: str, connection: list[str], **_):
|
|||||||
def user_show(username: str | None, all: bool, **_):
|
def user_show(username: str | None, all: bool, **_):
|
||||||
users = _resolve_users(username, all)
|
users = _resolve_users(username, all)
|
||||||
|
|
||||||
headers = ["#", "Username", "Exit Point", "Protocol"]
|
headers = ["#", "Username", "Target ID", "Protocol"]
|
||||||
rows = _build_user_rows(users)
|
rows = _build_user_rows(users)
|
||||||
|
|
||||||
print_table(headers, rows)
|
print_table(headers, rows)
|
||||||
@@ -139,10 +149,12 @@ def user_modify(username: str, to_add: list[str], to_delete: list[str], **_):
|
|||||||
if not isinstance(user, User):
|
if not isinstance(user, User):
|
||||||
raise ValueError(f"User not found: {username}")
|
raise ValueError(f"User not found: {username}")
|
||||||
|
|
||||||
add_key = [_parse_connection_spec(spec) for spec in to_add]
|
to_add_specs = [_parse_outbound_spec(spec) for spec in to_add]
|
||||||
del_key = [_parse_connection_spec(spec) for spec in to_delete]
|
to_del_specs = [_parse_outbound_spec(spec) for spec in to_delete]
|
||||||
|
|
||||||
user.modify_connection_by_key(xray_config, add_key, del_key)
|
user.modify_outbounds(
|
||||||
|
xray_config, target_storage, to_add_specs, to_del_specs
|
||||||
|
)
|
||||||
xray_config.modify_user(user)
|
xray_config.modify_user(user)
|
||||||
|
|
||||||
|
|
||||||
@@ -150,7 +162,7 @@ def user_delete(username: str | None, all: bool, **_):
|
|||||||
users = _resolve_users(username, all)
|
users = _resolve_users(username, all)
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
xray_config.delete_user(user.username)
|
xray_config.delete_user(user)
|
||||||
|
|
||||||
|
|
||||||
def profile_build(username: str | None, all: bool, **_):
|
def profile_build(username: str | None, all: bool, **_):
|
||||||
@@ -158,19 +170,19 @@ def profile_build(username: str | None, all: bool, **_):
|
|||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
try:
|
try:
|
||||||
profile = storage.load_profile(user.username)
|
profile = profile_storage.load_profile(user.username)
|
||||||
profile = ProfileFactory.from_user(user, profile.folder_name)
|
profile = ProfileFactory.from_user(user, profile.folder_name)
|
||||||
except (ValueError, FileNotFoundError) as e:
|
except (ValueError, FileNotFoundError) as e:
|
||||||
print(f"Cannot load profile for {user.username}: {e}")
|
print(f"Cannot load profile for {user.username}: {e}")
|
||||||
profile = ProfileFactory.from_user(user)
|
profile = ProfileFactory.from_user(user)
|
||||||
|
|
||||||
storage.save_profile(profile)
|
profile_storage.save_profile(profile)
|
||||||
|
|
||||||
|
|
||||||
def profile_show(username: str | None, all: bool, **_):
|
def profile_show(username: str | None, all: bool, **_):
|
||||||
profiles = _resolve_profiles(username, all)
|
profiles = _resolve_profiles(username, all)
|
||||||
|
|
||||||
headers = ["#", "Username", "Profile", "Exit Point", "Protocol"]
|
headers = ["#", "Username", "Profile", "Target ID", "Protocol"]
|
||||||
rows = _build_profile_rows(profiles)
|
rows = _build_profile_rows(profiles)
|
||||||
|
|
||||||
print_table(headers, rows)
|
print_table(headers, rows)
|
||||||
@@ -180,14 +192,14 @@ def profile_clear(username: str | None, all: bool, **_):
|
|||||||
profiles = _resolve_profiles(username, all)
|
profiles = _resolve_profiles(username, all)
|
||||||
|
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
storage.clear_profile(profile.user.username)
|
profile_storage.clear_profile(profile.user.username)
|
||||||
|
|
||||||
|
|
||||||
def profile_delete(username: str | None, all: bool, **_):
|
def profile_delete(username: str | None, all: bool, **_):
|
||||||
profiles = _resolve_profiles(username, all)
|
profiles = _resolve_profiles(username, all)
|
||||||
|
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
storage.delete_profile(profile.user.username)
|
profile_storage.delete_profile(profile.user.username)
|
||||||
|
|
||||||
|
|
||||||
def profile_qrcode(username: str | None, all: bool, **_):
|
def profile_qrcode(username: str | None, all: bool, **_):
|
||||||
|
|||||||
Reference in New Issue
Block a user