Удалил мусор и сертификаты вроде добавил
This commit is contained in:
@@ -24,13 +24,13 @@ class MongoDBRepository:
|
||||
self.logger.debug(f"Тарифный план добавлен с ID: {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:
|
||||
self.logger.debug(f"Найден тарифный план: {plan}")
|
||||
else:
|
||||
self.logger.error(f"Тарифный план {plan_id} не найден.")
|
||||
self.logger.error(f"Тарифный план {plan_name} не найден.")
|
||||
return plan
|
||||
|
||||
async def add_server(self, server_data):
|
||||
|
||||
@@ -132,80 +132,75 @@ class DatabaseManager:
|
||||
self.logger.error(f"Тарифный план {plan_id} не найден.")
|
||||
return "ERROR"
|
||||
|
||||
# Проверка достаточности средств для покупки подписки
|
||||
# Проверка достаточности средств
|
||||
cost = int(plan["price"])
|
||||
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:
|
||||
if result.balance < cost:
|
||||
self.logger.error(f"Недостаточно средств у пользователя {telegram_id} для покупки плана {plan_id}.")
|
||||
return "INSUFFICIENT_FUNDS"
|
||||
except SQLAlchemyError as e:
|
||||
self.logger.error(f"Ошибка при покупке подписки {plan_id} для пользователя {telegram_id}: {e}")
|
||||
await session.rollback()
|
||||
return "ERROR"
|
||||
|
||||
async def add_to_server(self, telegram_id: int):
|
||||
"""
|
||||
Метод для добавления пользователя на сервер.
|
||||
"""
|
||||
async for session in self.session_generator():
|
||||
try:
|
||||
# Получаем подписку пользователя по telegram_id
|
||||
result = await session.execute(select(Subscription).join(User).where(User.telegram_id == int(telegram_id)))
|
||||
user_sub = result.scalars().first()
|
||||
# Списываем средства
|
||||
result.balance -= cost
|
||||
|
||||
if not user_sub:
|
||||
self.logger.error(f"Не удалось найти подписку для пользователя с Telegram ID {telegram_id}.")
|
||||
return "ERROR"
|
||||
# Создаем подписку
|
||||
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 = 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}.")
|
||||
user = result # так как result уже содержит пользователя
|
||||
if not user:
|
||||
self.logger.error(f"Не удалось найти пользователя для добавления на сервер.")
|
||||
await session.rollback()
|
||||
return "ERROR"
|
||||
|
||||
# Доступ к данным сервера для добавления клиента
|
||||
server_info = server['server']
|
||||
# Получаем сервер из 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}.")
|
||||
await session.rollback()
|
||||
return "ERROR"
|
||||
|
||||
server_info = server_data['server']
|
||||
url_base = f"https://{server_info['ip']}:{server_info['port']}/{server_info['secretKey']}"
|
||||
login_data = {
|
||||
'username': server_info['login'],
|
||||
'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)
|
||||
|
||||
# Логируем результат
|
||||
if response == "OK":
|
||||
self.logger.info(f"Клиент {telegram_id} успешно добавлен на сервер.")
|
||||
return "OK"
|
||||
else:
|
||||
if response != "OK":
|
||||
self.logger.error(f"Ошибка при добавлении клиента {telegram_id} на сервер: {response}")
|
||||
# Если не получилось добавить на сервер, откатываем транзакцию
|
||||
await session.rollback()
|
||||
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"
|
||||
except Exception as e:
|
||||
self.logger.error(f"Непредвиденная ошибка: {e}")
|
||||
await session.rollback()
|
||||
return "ERROR"
|
||||
|
||||
|
||||
@staticmethod
|
||||
def generate_string(length):
|
||||
|
||||
@@ -24,7 +24,4 @@ services:
|
||||
PLAN_COLLECTION: "plans"
|
||||
volumes:
|
||||
- logs_data:/app/logs
|
||||
depends_on:
|
||||
- postgres
|
||||
- mongodb
|
||||
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
|
||||
}
|
||||
256
utils/panel.py
256
utils/panel.py
@@ -1,107 +1,187 @@
|
||||
import requests
|
||||
import aiohttp
|
||||
import uuid
|
||||
import string
|
||||
import secrets
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
import json
|
||||
import base64
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
def generate_uuid():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
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.login_data = login_data
|
||||
self.logger = logger_
|
||||
self.session_id = self.login()
|
||||
if self.session_id:
|
||||
self.headers = {
|
||||
'Accept': 'application/json',
|
||||
'Cookie': f'3x-ui={self.session_id}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
else:
|
||||
raise ValueError("Login failed, session_id is None")
|
||||
self.logger = logger
|
||||
self.cert_content = self._decode_certificate(certificate, is_encoded)
|
||||
self.session_id = None # Session ID will be initialized lazily
|
||||
self.headers = None
|
||||
|
||||
def login(self):
|
||||
login_url = self.base_url + "/login"
|
||||
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:
|
||||
self.headers = {
|
||||
'Accept': 'application/json',
|
||||
'Cookie': f'3x-ui={self.session_id}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
else:
|
||||
raise ValueError("Unable to log in and retrieve session ID.")
|
||||
|
||||
async def login(self):
|
||||
"""
|
||||
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}")
|
||||
try:
|
||||
response = requests.post(login_url, data=self.login_data, verify=False, timeout=10)
|
||||
response.raise_for_status()
|
||||
session_id = response.cookies.get("3x-ui")
|
||||
if session_id:
|
||||
return session_id
|
||||
else:
|
||||
self.logger.error(f"Login failed: {response.status_code}")
|
||||
self.logger.debug(f"Response content: {response.text}")
|
||||
return None
|
||||
except requests.RequestException as e:
|
||||
self.logger.error(f"Login request failed: {e}")
|
||||
return None
|
||||
|
||||
def getInboundInfo(self, inboundId):
|
||||
url = f"{self.base_url}/panel/api/inbounds/get/{inboundId}"
|
||||
try:
|
||||
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)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.post(
|
||||
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")
|
||||
if session_id:
|
||||
return session_id.value
|
||||
else:
|
||||
self.logger.error("Login failed: No session ID received.")
|
||||
return None
|
||||
else:
|
||||
self.logger.error(f"Login failed: {response.status}")
|
||||
return None
|
||||
except aiohttp.ClientError as e:
|
||||
self.logger.error(f"Login request failed: {e}")
|
||||
return None
|
||||
except requests.RequestException as e:
|
||||
self.logger.error(f"Get inbound request failed: {e}")
|
||||
|
||||
def get_client_traffic(self, email):
|
||||
|
||||
async def get_inbound_info(self, inbound_id):
|
||||
"""
|
||||
Fetch inbound information by ID.
|
||||
|
||||
: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}"
|
||||
try:
|
||||
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 client traffic: {response.status_code}")
|
||||
self.logger.debug("Response:", response.text)
|
||||
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 client traffic: {response.status}")
|
||||
return None
|
||||
except aiohttp.ClientError as e:
|
||||
self.logger.error(f"Get client traffic request failed: {e}")
|
||||
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"
|
||||
update_data = {
|
||||
"id": 1,
|
||||
"settings": json.dumps({
|
||||
"id": 1,
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": client_uuid,
|
||||
"alterId": 0,
|
||||
"email": client_email,
|
||||
"email": client_email,
|
||||
"limitIp": 2,
|
||||
"totalGB": 0,
|
||||
"totalGB": 0,
|
||||
"expiryTime": new_expiry_time,
|
||||
"enable": True,
|
||||
"tgId": "",
|
||||
"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"
|
||||
client_info = {
|
||||
"clients": [
|
||||
@@ -111,7 +191,7 @@ class PanelInteraction:
|
||||
"email": email,
|
||||
"limitIp": 2,
|
||||
"totalGB": 0,
|
||||
"flow":"xtls-rprx-vision",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"expiryTime": expiry_date,
|
||||
"enable": True,
|
||||
"tgId": "",
|
||||
@@ -121,15 +201,19 @@ class PanelInteraction:
|
||||
}
|
||||
payload = {
|
||||
"id": inbound_id,
|
||||
"settings": json.dumps(client_info)
|
||||
"settings": client_info
|
||||
}
|
||||
try:
|
||||
response = requests.post(url, headers=self.headers, json=payload, verify=False)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
self.logger.error(f"Failed to add client: {response.status_code}")
|
||||
self.logger.debug("Response:", response.text)
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.post(
|
||||
url, headers=self.headers, json=payload, ssl=self.cert_content
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
return await response.status
|
||||
else:
|
||||
self.logger.error(f"Failed to add client: {response.status}")
|
||||
return None
|
||||
except aiohttp.ClientError as e:
|
||||
self.logger.error(f"Add client request failed: {e}")
|
||||
return None
|
||||
finally:
|
||||
self.logger.info("Finished attempting to add client.")
|
||||
|
||||
Reference in New Issue
Block a user