Удалил мусор и сертификаты вроде добавил
This commit is contained in:
@@ -24,13 +24,13 @@ class MongoDBRepository:
|
|||||||
self.logger.debug(f"Тарифный план добавлен с ID: {result.inserted_id}")
|
self.logger.debug(f"Тарифный план добавлен с ID: {result.inserted_id}")
|
||||||
return result.inserted_id
|
return result.inserted_id
|
||||||
|
|
||||||
async def get_subscription_plan(self, plan_id):
|
async def get_subscription_plan(self, plan_name):
|
||||||
"""Получает тарифный план по его имени."""
|
"""Получает тарифный план по его имени."""
|
||||||
plan = await self.plans_collection.find_one({"_id": plan_id})
|
plan = await self.plans_collection.find_one({"name": plan_name})
|
||||||
if plan:
|
if plan:
|
||||||
self.logger.debug(f"Найден тарифный план: {plan}")
|
self.logger.debug(f"Найден тарифный план: {plan}")
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"Тарифный план {plan_id} не найден.")
|
self.logger.error(f"Тарифный план {plan_name} не найден.")
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
async def add_server(self, server_data):
|
async def add_server(self, server_data):
|
||||||
|
|||||||
@@ -132,80 +132,75 @@ class DatabaseManager:
|
|||||||
self.logger.error(f"Тарифный план {plan_id} не найден.")
|
self.logger.error(f"Тарифный план {plan_id} не найден.")
|
||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
|
||||||
# Проверка достаточности средств для покупки подписки
|
# Проверка достаточности средств
|
||||||
cost = int(plan["price"])
|
cost = int(plan["price"])
|
||||||
if result.balance >= cost:
|
if result.balance < cost:
|
||||||
result.balance -= cost
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
# Создание подписки для пользователя
|
|
||||||
expiry_date = datetime.utcnow() + relativedelta(months=plan["duration_months"])
|
|
||||||
server = await self.mongo_repo.get_server_with_least_clients()
|
|
||||||
self.logger.info(f"{server}")
|
|
||||||
new_subscription = Subscription(user_id=result.id, vpn_server_id=str(server['server']["name"]), plan=plan_id, expiry_date=expiry_date)
|
|
||||||
session.add(new_subscription)
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
self.logger.info(f"Подписка успешно оформлена для пользователя {telegram_id} на план {plan_id}.")
|
|
||||||
return "OK"
|
|
||||||
else:
|
|
||||||
self.logger.error(f"Недостаточно средств у пользователя {telegram_id} для покупки плана {plan_id}.")
|
self.logger.error(f"Недостаточно средств у пользователя {telegram_id} для покупки плана {plan_id}.")
|
||||||
return "INSUFFICIENT_FUNDS"
|
return "INSUFFICIENT_FUNDS"
|
||||||
except SQLAlchemyError as e:
|
|
||||||
self.logger.error(f"Ошибка при покупке подписки {plan_id} для пользователя {telegram_id}: {e}")
|
# Списываем средства
|
||||||
|
result.balance -= cost
|
||||||
|
|
||||||
|
# Создаем подписку
|
||||||
|
expiry_date = datetime.utcnow() + relativedelta(months=plan["duration_months"])
|
||||||
|
server = await self.mongo_repo.get_server_with_least_clients()
|
||||||
|
self.logger.info(f"Выбран сервер для подписки: {server}")
|
||||||
|
new_subscription = Subscription(
|
||||||
|
user_id=result.id,
|
||||||
|
vpn_server_id=str(server['server']["name"]),
|
||||||
|
plan=plan_id,
|
||||||
|
expiry_date=expiry_date
|
||||||
|
)
|
||||||
|
session.add(new_subscription)
|
||||||
|
|
||||||
|
# Попытка добавить пользователя на сервер
|
||||||
|
# Получаем информацию о пользователе
|
||||||
|
user = result # так как result уже содержит пользователя
|
||||||
|
if not user:
|
||||||
|
self.logger.error(f"Не удалось найти пользователя для добавления на сервер.")
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
|
||||||
async def add_to_server(self, telegram_id: int):
|
# Получаем сервер из MongoDB
|
||||||
"""
|
server_data = await self.mongo_repo.get_server(new_subscription.vpn_server_id)
|
||||||
Метод для добавления пользователя на сервер.
|
if not server_data:
|
||||||
"""
|
self.logger.error(f"Не удалось найти сервер с ID {new_subscription.vpn_server_id}.")
|
||||||
async for session in self.session_generator():
|
await session.rollback()
|
||||||
try:
|
|
||||||
# Получаем подписку пользователя по telegram_id
|
|
||||||
result = await session.execute(select(Subscription).join(User).where(User.telegram_id == int(telegram_id)))
|
|
||||||
user_sub = result.scalars().first()
|
|
||||||
|
|
||||||
if not user_sub:
|
|
||||||
self.logger.error(f"Не удалось найти подписку для пользователя с Telegram ID {telegram_id}.")
|
|
||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
|
||||||
# Получаем информацию о пользователе
|
server_info = server_data['server']
|
||||||
user_result = await session.execute(select(User).where(User.telegram_id == telegram_id))
|
|
||||||
user = user_result.scalars().first()
|
|
||||||
|
|
||||||
# Получаем сервер с MongoDB
|
|
||||||
server = await self.mongo_repo.get_server(user_sub.vpn_server_id)
|
|
||||||
|
|
||||||
if not server:
|
|
||||||
self.logger.error(f"Не удалось найти сервер с ID {user_sub.vpn_server_id}.")
|
|
||||||
return "ERROR"
|
|
||||||
|
|
||||||
# Доступ к данным сервера для добавления клиента
|
|
||||||
server_info = server['server']
|
|
||||||
url_base = f"https://{server_info['ip']}:{server_info['port']}/{server_info['secretKey']}"
|
url_base = f"https://{server_info['ip']}:{server_info['port']}/{server_info['secretKey']}"
|
||||||
login_data = {
|
login_data = {
|
||||||
'username': server_info['login'],
|
'username': server_info['login'],
|
||||||
'password': server_info['password'],
|
'password': server_info['password'],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Инициализируем взаимодействие с панелью управления
|
panel = PanelInteraction(url_base, login_data, self.logger,)
|
||||||
panel = PanelInteraction(url_base, login_data, self.logger)
|
expiry_date_iso = new_subscription.expiry_date.isoformat()
|
||||||
|
|
||||||
expiry_date_iso = user_sub.expiry_date.isoformat()
|
# Добавляем на сервер
|
||||||
response = await panel.add_client(user.id, expiry_date_iso, user.username)
|
response = await panel.add_client(user.id, expiry_date_iso, user.username)
|
||||||
|
|
||||||
# Логируем результат
|
if response != "OK":
|
||||||
if response == "OK":
|
|
||||||
self.logger.info(f"Клиент {telegram_id} успешно добавлен на сервер.")
|
|
||||||
return "OK"
|
|
||||||
else:
|
|
||||||
self.logger.error(f"Ошибка при добавлении клиента {telegram_id} на сервер: {response}")
|
self.logger.error(f"Ошибка при добавлении клиента {telegram_id} на сервер: {response}")
|
||||||
|
# Если не получилось добавить на сервер, откатываем транзакцию
|
||||||
|
await session.rollback()
|
||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
|
||||||
except Exception as e:
|
# Если мы здесь - значит и подписка, и добавление на сервер успешны
|
||||||
self.logger.error(f"Ошибка при установке на сервер для пользователя {telegram_id}: {e}")
|
await session.commit()
|
||||||
|
self.logger.info(f"Подписка успешно оформлена для пользователя {telegram_id} на план {plan_id} и клиент добавлен на сервер.")
|
||||||
|
return "OK"
|
||||||
|
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
self.logger.error(f"Ошибка при покупке подписки {plan_id} для пользователя {telegram_id}: {e}")
|
||||||
|
await session.rollback()
|
||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Непредвиденная ошибка: {e}")
|
||||||
|
await session.rollback()
|
||||||
|
return "ERROR"
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_string(length):
|
def generate_string(length):
|
||||||
|
|||||||
@@ -24,7 +24,4 @@ services:
|
|||||||
PLAN_COLLECTION: "plans"
|
PLAN_COLLECTION: "plans"
|
||||||
volumes:
|
volumes:
|
||||||
- logs_data:/app/logs
|
- logs_data:/app/logs
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
- mongodb
|
|
||||||
command: ["python", "main.py"]
|
command: ["python", "main.py"]
|
||||||
|
|||||||
@@ -1,263 +0,0 @@
|
|||||||
from db import User, Subscription, Transaction, VPNServer
|
|
||||||
import string
|
|
||||||
import secrets
|
|
||||||
import json
|
|
||||||
from sqlalchemy import desc
|
|
||||||
from dateutil.relativedelta import relativedelta
|
|
||||||
from datetime import datetime
|
|
||||||
from db import get_db_session
|
|
||||||
from panel import PanelInteraction
|
|
||||||
|
|
||||||
from utils.LogCon import setup_logger, load_config
|
|
||||||
|
|
||||||
config = load_config()
|
|
||||||
|
|
||||||
def generate_random_string(length=8):
|
|
||||||
characters = string.ascii_letters + string.digits
|
|
||||||
return ''.join(secrets.choice(characters) for _ in range(length))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserService:
|
|
||||||
def __init__(self, logger):
|
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
def add_user(self, telegram_id: int):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
new_user = User(telegram_id=telegram_id, username=generate_random_string())
|
|
||||||
session.add(new_user)
|
|
||||||
session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
session.rollback()
|
|
||||||
self.logger.error(f"Ошибка при добавлении пользователя: {e}")
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def get_user_by_telegram_id(self, telegram_id: int):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
return session.query(User).filter(User.telegram_id == telegram_id).first()
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка при получении пользователя: {e}")
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def add_transaction(self, user_id: int, amount: float):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
transaction = Transaction(user_id=user_id, amount=amount)
|
|
||||||
session.add(transaction)
|
|
||||||
session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка добавления транзакции: {e}")
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def update_balance(self, telegram_id: int, amount: float):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
|
||||||
if user:
|
|
||||||
user.balance = amount
|
|
||||||
self.add_transaction(user.id, amount)
|
|
||||||
session.commit()
|
|
||||||
else:
|
|
||||||
self.logger.warning(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
|
||||||
except Exception as e:
|
|
||||||
session.rollback()
|
|
||||||
self.logger.error(f"Ошибка при обновлении баланса: {e}")
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def last_subscription(self, user):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
return (
|
|
||||||
session.query(Subscription)
|
|
||||||
.filter(Subscription.user_id == user.id)
|
|
||||||
.order_by(desc(Subscription.created_at))
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка при получении последней подписки: {e}")
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def tariff_setting(self, user, plan: str, expiry_duration: int):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
server = (
|
|
||||||
session.query(VPNServer)
|
|
||||||
.filter(VPNServer.current_users < VPNServer.max_users)
|
|
||||||
.order_by(VPNServer.current_users.asc())
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
|
|
||||||
if not server:
|
|
||||||
self.logger.error("Error: 120")
|
|
||||||
return "120"
|
|
||||||
|
|
||||||
# Рассчитываем дату окончания подписки
|
|
||||||
expiry_ = datetime.utcnow() + relativedelta(months=expiry_duration)
|
|
||||||
self.logger.info(f"Create subscribe to {user.id} on server {server.id} with plan {plan} until {expiry_}")
|
|
||||||
|
|
||||||
new_subscription = Subscription(user_id=user.id, vpn_server_id=server.id, plan=plan, expiry_date=expiry_)
|
|
||||||
session.add(new_subscription)
|
|
||||||
session.commit()
|
|
||||||
|
|
||||||
self.logger.info(f"Subscribe successfully created for {user.id}")
|
|
||||||
return "OK"
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error with created subscribe: {e}")
|
|
||||||
return "Ошибка"
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def buy_sub(self, telegram_id: str, plan: str):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
|
||||||
if not user:
|
|
||||||
self.logger.error(f"User with Telegram ID {telegram_id} not found.")
|
|
||||||
return "error"
|
|
||||||
|
|
||||||
current_plan = config['subscription_templates'].get(plan)
|
|
||||||
if not current_plan:
|
|
||||||
self.logger.error(f"Tarif {plan} not found.")
|
|
||||||
return "error"
|
|
||||||
|
|
||||||
cost = current_plan['cost']
|
|
||||||
if user.balance >= cost:
|
|
||||||
user.balance -= cost
|
|
||||||
session.commit()
|
|
||||||
result = self.tariff_setting(user, plan, current_plan['duration'])
|
|
||||||
if result == "OK":
|
|
||||||
add_server_result = self.add_to_server(telegram_id)
|
|
||||||
if add_server_result == "OK":
|
|
||||||
return "OK"
|
|
||||||
else:
|
|
||||||
return "ERROR " + add_server_result
|
|
||||||
else:
|
|
||||||
return "ERROR " + result
|
|
||||||
|
|
||||||
self.logger.error(f"Nt enough money {telegram_id} for {plan}.")
|
|
||||||
return 100
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error with buying sub {telegram_id}: {e}")
|
|
||||||
session.rollback()
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def get_sub_list(self, count: int, user_id: int):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
return (
|
|
||||||
session.query(Subscription)
|
|
||||||
.filter(Subscription.user_id == user_id)
|
|
||||||
.order_by(desc(Subscription.created_at))
|
|
||||||
.limit(count)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка при получении списка подписок для пользователя {user_id}: {e}")
|
|
||||||
|
|
||||||
def add_to_server(self, telegram_id: str):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
user_sub = (
|
|
||||||
session.query(Subscription)
|
|
||||||
.join(User)
|
|
||||||
.filter(User.telegram_id == telegram_id)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
|
||||||
server = session.query(VPNServer).filter(VPNServer.id == user_sub.vpn_server_id).first()
|
|
||||||
|
|
||||||
url_base = f"https://{server.ip_address}:{server.port}/{server.secret}"
|
|
||||||
login_data = {
|
|
||||||
'username': server.login,
|
|
||||||
'password': server.password,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
server_config_dict = json.loads(server.config)
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
self.logger.error(f"Ошибка разбора JSON: {e}")
|
|
||||||
return "180"
|
|
||||||
|
|
||||||
client_id = server_config_dict['obj']['id']
|
|
||||||
panel = PanelInteraction(url_base, login_data, self.logger)
|
|
||||||
panel.add_client(client_id, user_sub.expiry_date.isoformat(), user.username)
|
|
||||||
return "OK"
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка при установке на сервер для пользователя {telegram_id}: {e}")
|
|
||||||
return "ERROR"
|
|
||||||
|
|
||||||
def create_uri(self, telegram_id: str):
|
|
||||||
session = next(get_db_session())
|
|
||||||
try:
|
|
||||||
user = session.query(User).filter(User.telegram_id == telegram_id).first()
|
|
||||||
if not user:
|
|
||||||
self.logger.error(f"Пользователь с Telegram ID {telegram_id} не найден.")
|
|
||||||
return "error"
|
|
||||||
|
|
||||||
sub = self.last_subscription(user)
|
|
||||||
if not sub:
|
|
||||||
self.logger.error("Подписка не найдена.")
|
|
||||||
return "error"
|
|
||||||
|
|
||||||
vpn_server = session.query(VPNServer).filter_by(id=sub.vpn_server_id).first()
|
|
||||||
base_url = f"https://{vpn_server.ip_address}:{vpn_server.port}/{vpn_server.secret}"
|
|
||||||
login_data = {
|
|
||||||
'username': vpn_server.login,
|
|
||||||
'password': vpn_server.password
|
|
||||||
}
|
|
||||||
|
|
||||||
server_config_dict = json.loads(vpn_server.config)
|
|
||||||
client_id = server_config_dict['obj']['id']
|
|
||||||
|
|
||||||
PI = PanelInteraction(base_url, login_data, self.logger)
|
|
||||||
CIF3 = PI.get_client_traffic(user.username) # Client Info From 3x-ui
|
|
||||||
VPNCIF3 = PI.getInboundInfo(client_id)
|
|
||||||
return self.generate_uri(vpn_config=VPNCIF3, CIF3=CIF3)
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка в создании URI: {e}")
|
|
||||||
return "error"
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
def generate_uri(self, vpn_config, CIF3):
|
|
||||||
try:
|
|
||||||
config_data = json.loads(vpn_config) if isinstance(vpn_config, str) else vpn_config
|
|
||||||
|
|
||||||
obj = config_data["obj"]
|
|
||||||
port = obj["port"]
|
|
||||||
|
|
||||||
clients = json.loads(obj["settings"])["clients"] if isinstance(obj["settings"], str) else obj["settings"]["clients"]
|
|
||||||
|
|
||||||
for client in clients:
|
|
||||||
if client["email"] == CIF3['obj']['email']:
|
|
||||||
uuid = client["id"]
|
|
||||||
flow = client["flow"]
|
|
||||||
|
|
||||||
stream_settings = json.loads(obj["streamSettings"]) if isinstance(obj["streamSettings"], str) else obj["streamSettings"]
|
|
||||||
dest = stream_settings["realitySettings"]["dest"]
|
|
||||||
server_names = stream_settings["realitySettings"]["serverNames"]
|
|
||||||
public_key = stream_settings["realitySettings"]["settings"]["publicKey"]
|
|
||||||
fingerprint = stream_settings["realitySettings"]["settings"]["fingerprint"]
|
|
||||||
short_id = stream_settings["realitySettings"]["shortIds"][0] # Первый короткий ID
|
|
||||||
|
|
||||||
return (
|
|
||||||
f"vless://{uuid}@{dest}:{port}?type=tcp&security=reality"
|
|
||||||
f"&pbk={public_key}&fp={fingerprint}&sni={server_names[0]}"
|
|
||||||
f"&sid={short_id}&spx=%2F&flow={flow}#user-{CIF3}"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.logger.error(f"Клиент с email {CIF3} не найден.")
|
|
||||||
return None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Ошибка в методе создания URI: {e}")
|
|
||||||
return None
|
|
||||||
37
temp_scripts/ca.crt
Normal file
37
temp_scripts/ca.crt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
MYVC3EGeng==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
74
temp_scripts/insert_subscriptions.py
Normal file
74
temp_scripts/insert_subscriptions.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import argparse
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
import glob
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
def connect_to_mongo(uri, db_name):
|
||||||
|
"""Подключение к MongoDB."""
|
||||||
|
client = MongoClient(uri)
|
||||||
|
db = client[db_name]
|
||||||
|
return db
|
||||||
|
|
||||||
|
def load_all_json_from_folder(folder_path):
|
||||||
|
"""Загружает все JSON-файлы из указанной папки."""
|
||||||
|
all_data = []
|
||||||
|
for file_path in glob.glob(f"{folder_path}/*.json"):
|
||||||
|
try:
|
||||||
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
all_data.append(data)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка при чтении файла {file_path}: {e}")
|
||||||
|
return all_data
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_all_documents(mongo_uri, db_name, collection_name):
|
||||||
|
"""Выводит все элементы из указанной коллекции MongoDB."""
|
||||||
|
try:
|
||||||
|
client = MongoClient(mongo_uri)
|
||||||
|
db = client[db_name]
|
||||||
|
collection = db[collection_name]
|
||||||
|
|
||||||
|
documents = collection.find()
|
||||||
|
|
||||||
|
print(f"Содержимое коллекции '{collection_name}':")
|
||||||
|
for doc in documents:
|
||||||
|
print(doc)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка при получении данных: {e}")
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
def insert_data(db, collection_name, data):
|
||||||
|
"""Вставляет данные в указанную коллекцию MongoDB."""
|
||||||
|
collection = db[collection_name]
|
||||||
|
for i in data:
|
||||||
|
collection.insert_one(i)
|
||||||
|
print(f"Данные '{i}'")
|
||||||
|
print(f"Данные успешно вставлены в коллекцию '{collection_name}'.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Insert JSON data into MongoDB with certificate")
|
||||||
|
parser.add_argument("--mongo-uri",default="mongodb://root:itOj4CE2miKR@mongodb:27017" ,required=True, help="MongoDB URI")
|
||||||
|
parser.add_argument("--db-name",default="MongoDBSub&Ser" ,required=True, help="MongoDB database name")
|
||||||
|
parser.add_argument("--collection",default="servers", required=True, help="Collection name")
|
||||||
|
parser.add_argument("--json-path", required=True, help="Path to the JSON file with data")
|
||||||
|
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
db = connect_to_mongo(args.mongo_uri, args.db_name)
|
||||||
|
|
||||||
|
data = load_all_json_from_folder(args.json_path)
|
||||||
|
|
||||||
|
insert_data(db, args.collection, data)
|
||||||
|
|
||||||
|
fetch_all_documents(args.mongo_uri, args.db_name,args.collection)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
68
temp_scripts/ser.json
Normal file
68
temp_scripts/ser.json
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"name": "Poland#1",
|
||||||
|
"ip": "mydomvlv.freemyip.com",
|
||||||
|
"port": 2053,
|
||||||
|
"secretKey": "Hd8OsqN5Jh",
|
||||||
|
"login": "nc1450nP",
|
||||||
|
"password": "KmajQOuf"
|
||||||
|
},
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"email": "j8oajwd3",
|
||||||
|
"inboundId": "1",
|
||||||
|
"id": "a31d71a4-6afd-4f36-96f6-860691b52873",
|
||||||
|
"flow": "xtls-rprx-vision",
|
||||||
|
"limits": {
|
||||||
|
"ipLimit": 2,
|
||||||
|
"reset": 0,
|
||||||
|
"totalGB": 0
|
||||||
|
},
|
||||||
|
"subscriptions": {
|
||||||
|
"subId": "ox2awiqwduryuqnz",
|
||||||
|
"tgId": 1342351277
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "j8oajwd3121",
|
||||||
|
"inboundId": "1",
|
||||||
|
"id": "b6882942-d69d-4d5e-be9a-168ac89b20b6",
|
||||||
|
"flow": "xtls-rprx-direct",
|
||||||
|
"limits": {
|
||||||
|
"ipLimit": 1,
|
||||||
|
"reset": 0,
|
||||||
|
"totalGB": 0
|
||||||
|
},
|
||||||
|
"subscriptions": {
|
||||||
|
"subId": "jk289x00uf7vbr9x",
|
||||||
|
"tgId": 123144325
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connection": {
|
||||||
|
"destination": "google.com:443",
|
||||||
|
"serverNames": [
|
||||||
|
"google.com",
|
||||||
|
"www.google.com"
|
||||||
|
],
|
||||||
|
"security": "reality",
|
||||||
|
"publicKey": "Bha0eW7nfRc69CdZyF9HlmGVvtAeOJKammhwf4WShTU",
|
||||||
|
"fingerprint": "random",
|
||||||
|
"shortIds": [
|
||||||
|
"edfaf8ab"
|
||||||
|
],
|
||||||
|
"tcpSettings": {
|
||||||
|
"acceptProxyProtocol": false,
|
||||||
|
"headerType": "none"
|
||||||
|
},
|
||||||
|
"sniffing": {
|
||||||
|
"enabled": true,
|
||||||
|
"destOverride": [
|
||||||
|
"http",
|
||||||
|
"tls",
|
||||||
|
"quic",
|
||||||
|
"fakedns"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
temp_scripts/subs/sub1.json
Normal file
8
temp_scripts/subs/sub1.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Lark_Standart_1",
|
||||||
|
"normalName": "Lark Standart",
|
||||||
|
"type": "standart",
|
||||||
|
"duration_months": 1,
|
||||||
|
"ip_limit": 1,
|
||||||
|
"price": 200
|
||||||
|
}
|
||||||
8
temp_scripts/subs/sub2.json
Normal file
8
temp_scripts/subs/sub2.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Lark_Standart_6",
|
||||||
|
"normalName": "Lark Standart",
|
||||||
|
"type": "standart",
|
||||||
|
"duration_months": 6,
|
||||||
|
"ip_limit": 1,
|
||||||
|
"price": 1000
|
||||||
|
}
|
||||||
8
temp_scripts/subs/sub3.json
Normal file
8
temp_scripts/subs/sub3.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Lark_Standart_12",
|
||||||
|
"normalName": "Lark Standart",
|
||||||
|
"type": "standart",
|
||||||
|
"duration_months": 12,
|
||||||
|
"ip_limit": 1,
|
||||||
|
"price": 2000
|
||||||
|
}
|
||||||
8
temp_scripts/subs/sub4.json
Normal file
8
temp_scripts/subs/sub4.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Lark_Pro_1",
|
||||||
|
"normalName": "Lark Pro",
|
||||||
|
"type": "pro",
|
||||||
|
"duration_months": 1,
|
||||||
|
"ip_limit": 5,
|
||||||
|
"price": 600
|
||||||
|
}
|
||||||
8
temp_scripts/subs/sub5.json
Normal file
8
temp_scripts/subs/sub5.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Lark_Pro_6",
|
||||||
|
"normalName": "Lark Pro",
|
||||||
|
"type": "pro",
|
||||||
|
"duration_months": 6,
|
||||||
|
"ip_limit": 5,
|
||||||
|
"price": 3000
|
||||||
|
}
|
||||||
8
temp_scripts/subs/sub6.json
Normal file
8
temp_scripts/subs/sub6.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Lark_Pro_12",
|
||||||
|
"normalName": "Lark Pro",
|
||||||
|
"type": "pro",
|
||||||
|
"duration_months": 12,
|
||||||
|
"ip_limit": 5,
|
||||||
|
"price": 5000
|
||||||
|
}
|
||||||
208
utils/panel.py
208
utils/panel.py
@@ -1,22 +1,52 @@
|
|||||||
import requests
|
import aiohttp
|
||||||
import uuid
|
import uuid
|
||||||
import string
|
|
||||||
import secrets
|
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, timedelta
|
import base64
|
||||||
|
from datetime import datetime
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_uuid():
|
def generate_uuid():
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
class PanelInteraction:
|
class PanelInteraction:
|
||||||
def __init__(self, base_url, login_data, logger_):
|
def __init__(self, base_url, login_data, logger, certificate=None, is_encoded=True):
|
||||||
|
"""
|
||||||
|
Initialize the PanelInteraction class.
|
||||||
|
|
||||||
|
:param base_url: Base URL for the panel.
|
||||||
|
:param login_data: Login data (username/password or token).
|
||||||
|
:param logger: Logger for debugging.
|
||||||
|
:param certificate: Certificate content (Base64-encoded or raw string).
|
||||||
|
:param is_encoded: Indicates whether the certificate is Base64-encoded.
|
||||||
|
"""
|
||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
self.login_data = login_data
|
self.login_data = login_data
|
||||||
self.logger = logger_
|
self.logger = logger
|
||||||
self.session_id = self.login()
|
self.cert_content = self._decode_certificate(certificate, is_encoded)
|
||||||
|
self.session_id = None # Session ID will be initialized lazily
|
||||||
|
self.headers = None
|
||||||
|
|
||||||
|
def _decode_certificate(self, certificate, is_encoded):
|
||||||
|
"""
|
||||||
|
Decode the provided certificate content.
|
||||||
|
|
||||||
|
:param certificate: Certificate content (Base64-encoded or raw string).
|
||||||
|
:param is_encoded: Indicates whether the certificate is Base64-encoded.
|
||||||
|
:return: Decoded certificate content as bytes.
|
||||||
|
"""
|
||||||
|
if not certificate:
|
||||||
|
self.logger.error("No certificate provided.")
|
||||||
|
raise ValueError("Certificate is required.")
|
||||||
|
|
||||||
|
return base64.b64decode(certificate) if is_encoded else certificate.encode()
|
||||||
|
|
||||||
|
async def _ensure_logged_in(self):
|
||||||
|
"""
|
||||||
|
Ensure the session ID is available for authenticated requests.
|
||||||
|
"""
|
||||||
|
if not self.session_id:
|
||||||
|
self.session_id = await self.login()
|
||||||
if self.session_id:
|
if self.session_id:
|
||||||
self.headers = {
|
self.headers = {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
@@ -24,58 +54,96 @@ class PanelInteraction:
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise ValueError("Login failed, session_id is None")
|
raise ValueError("Unable to log in and retrieve session ID.")
|
||||||
|
|
||||||
def login(self):
|
async def login(self):
|
||||||
login_url = self.base_url + "/login"
|
"""
|
||||||
|
Perform login to the panel.
|
||||||
|
|
||||||
|
:return: Session ID or None.
|
||||||
|
"""
|
||||||
|
login_url = f"{self.base_url}/login"
|
||||||
self.logger.info(f"Attempting to login at: {login_url}")
|
self.logger.info(f"Attempting to login at: {login_url}")
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
try:
|
try:
|
||||||
response = requests.post(login_url, data=self.login_data, verify=False, timeout=10)
|
async with session.post(
|
||||||
response.raise_for_status()
|
login_url, data=self.login_data, ssl=self.cert_content, timeout=10
|
||||||
|
) as response:
|
||||||
|
if response.status == 200:
|
||||||
session_id = response.cookies.get("3x-ui")
|
session_id = response.cookies.get("3x-ui")
|
||||||
if session_id:
|
if session_id:
|
||||||
return session_id
|
return session_id.value
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"Login failed: {response.status_code}")
|
self.logger.error("Login failed: No session ID received.")
|
||||||
self.logger.debug(f"Response content: {response.text}")
|
|
||||||
return None
|
return None
|
||||||
except requests.RequestException as e:
|
else:
|
||||||
|
self.logger.error(f"Login failed: {response.status}")
|
||||||
|
return None
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
self.logger.error(f"Login request failed: {e}")
|
self.logger.error(f"Login request failed: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getInboundInfo(self, inboundId):
|
async def get_inbound_info(self, inbound_id):
|
||||||
url = f"{self.base_url}/panel/api/inbounds/get/{inboundId}"
|
"""
|
||||||
try:
|
Fetch inbound information by ID.
|
||||||
response = requests.get(url, headers=self.headers, verify=False, timeout=10)
|
|
||||||
response.raise_for_status()
|
|
||||||
if response:
|
|
||||||
return response.json()
|
|
||||||
else:
|
|
||||||
self.logger.error(f"Failed to get inbound info: {response.status_code}")
|
|
||||||
self.logger.debug("Response:", response.text)
|
|
||||||
return None
|
|
||||||
except requests.RequestException as e:
|
|
||||||
self.logger.error(f"Get inbound request failed: {e}")
|
|
||||||
|
|
||||||
def get_client_traffic(self, email):
|
:param inbound_id: ID of the inbound.
|
||||||
|
:return: JSON response or None.
|
||||||
|
"""
|
||||||
|
await self._ensure_logged_in()
|
||||||
|
url = f"{self.base_url}/panel/api/inbounds/get/{inbound_id}"
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
try:
|
||||||
|
async with session.get(
|
||||||
|
url, headers=self.headers, ssl=self.cert_content, timeout=10
|
||||||
|
) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
return await response.json()
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Failed to get inbound info: {response.status}")
|
||||||
|
return None
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
self.logger.error(f"Get inbound info request failed: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_client_traffic(self, email):
|
||||||
|
"""
|
||||||
|
Fetch traffic information for a specific client.
|
||||||
|
|
||||||
|
:param email: Client's email.
|
||||||
|
:return: JSON response or None.
|
||||||
|
"""
|
||||||
|
await self._ensure_logged_in()
|
||||||
url = f"{self.base_url}/panel/api/inbounds/getClientTraffics/{email}"
|
url = f"{self.base_url}/panel/api/inbounds/getClientTraffics/{email}"
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, headers=self.headers, verify=False, timeout=10)
|
async with session.get(
|
||||||
response.raise_for_status()
|
url, headers=self.headers, ssl=self.cert_content, timeout=10
|
||||||
if response:
|
) as response:
|
||||||
return response.json()
|
if response.status == 200:
|
||||||
|
return await response.json()
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"Failed to get client traffic: {response.status_code}")
|
self.logger.error(f"Failed to get client traffic: {response.status}")
|
||||||
self.logger.debug("Response:", response.text)
|
return None
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
self.logger.error(f"Get client traffic request failed: {e}")
|
||||||
return None
|
return None
|
||||||
except requests.RequestException as e:
|
|
||||||
self.loggin.error(f"Get client request failed: {e}")
|
|
||||||
|
|
||||||
def update_client_expiry(self, client_uuid, new_expiry_time, client_email):
|
async def update_client_expiry(self, client_uuid, new_expiry_time, client_email):
|
||||||
|
"""
|
||||||
|
Update the expiry date of a specific client.
|
||||||
|
|
||||||
|
:param client_uuid: UUID of the client.
|
||||||
|
:param new_expiry_time: New expiry date in ISO format.
|
||||||
|
:param client_email: Client's email.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
await self._ensure_logged_in()
|
||||||
url = f"{self.base_url}/panel/api/inbounds/updateClient"
|
url = f"{self.base_url}/panel/api/inbounds/updateClient"
|
||||||
update_data = {
|
update_data = {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"settings": json.dumps({
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": client_uuid,
|
"id": client_uuid,
|
||||||
@@ -89,19 +157,31 @@ class PanelInteraction:
|
|||||||
"subId": ""
|
"subId": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
|
||||||
}
|
}
|
||||||
try:
|
}
|
||||||
response = requests.post(url, headers=self.headers, json=update_data, verify=False)
|
|
||||||
response.raise_for_status()
|
|
||||||
if response:
|
|
||||||
self.logger.debug("Client expiry time updated successfully.")
|
|
||||||
else:
|
|
||||||
self.logger.error(f"Failed to update client: {response.status_code}, {response.text}")
|
|
||||||
except requests.RequestException as e:
|
|
||||||
self.logger.error(f"Update client request failed: {e}")
|
|
||||||
|
|
||||||
def add_client(self, inbound_id, expiry_date,email):
|
async with aiohttp.ClientSession() as session:
|
||||||
|
try:
|
||||||
|
async with session.post(
|
||||||
|
url, headers=self.headers, json=update_data, ssl=self.cert_content
|
||||||
|
) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
self.logger.info("Client expiry updated successfully.")
|
||||||
|
else:
|
||||||
|
self.logger.error(f"Failed to update client expiry: {response.status}")
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
self.logger.error(f"Update client expiry request failed: {e}")
|
||||||
|
|
||||||
|
async def add_client(self, inbound_id, expiry_date, email):
|
||||||
|
"""
|
||||||
|
Add a new client to an inbound.
|
||||||
|
|
||||||
|
:param inbound_id: ID of the inbound.
|
||||||
|
:param expiry_date: Expiry date in ISO format.
|
||||||
|
:param email: Client's email.
|
||||||
|
:return: JSON response or None.
|
||||||
|
"""
|
||||||
|
await self._ensure_logged_in()
|
||||||
url = f"{self.base_url}/panel/api/inbounds/addClient"
|
url = f"{self.base_url}/panel/api/inbounds/addClient"
|
||||||
client_info = {
|
client_info = {
|
||||||
"clients": [
|
"clients": [
|
||||||
@@ -111,7 +191,7 @@ class PanelInteraction:
|
|||||||
"email": email,
|
"email": email,
|
||||||
"limitIp": 2,
|
"limitIp": 2,
|
||||||
"totalGB": 0,
|
"totalGB": 0,
|
||||||
"flow":"xtls-rprx-vision",
|
"flow": "xtls-rprx-vision",
|
||||||
"expiryTime": expiry_date,
|
"expiryTime": expiry_date,
|
||||||
"enable": True,
|
"enable": True,
|
||||||
"tgId": "",
|
"tgId": "",
|
||||||
@@ -121,15 +201,19 @@ class PanelInteraction:
|
|||||||
}
|
}
|
||||||
payload = {
|
payload = {
|
||||||
"id": inbound_id,
|
"id": inbound_id,
|
||||||
"settings": json.dumps(client_info)
|
"settings": client_info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
try:
|
try:
|
||||||
response = requests.post(url, headers=self.headers, json=payload, verify=False)
|
async with session.post(
|
||||||
if response.status_code == 200:
|
url, headers=self.headers, json=payload, ssl=self.cert_content
|
||||||
return response.json()
|
) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
return await response.status
|
||||||
else:
|
else:
|
||||||
self.logger.error(f"Failed to add client: {response.status_code}")
|
self.logger.error(f"Failed to add client: {response.status}")
|
||||||
self.logger.debug("Response:", response.text)
|
return None
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
self.logger.error(f"Add client request failed: {e}")
|
||||||
return None
|
return None
|
||||||
finally:
|
|
||||||
self.logger.info("Finished attempting to add client.")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user