diff --git a/config.json b/config.json index 619dc04..b11cc8c 100644 --- a/config.json +++ b/config.json @@ -13,8 +13,11 @@ } }, "auth": { - "type": "command", - "command": "/etc/hysteria/core/scripts/hysteria2/user.sh" + "type": "http", + "http": { + "url": "http://127.0.0.1:28262/auth", + "timeout": "5s" + } }, "quic": { "initStreamReceiveWindow": 8388608, diff --git a/core/scripts/hysteria2/auth_server.py b/core/scripts/hysteria2/auth_server.py new file mode 100644 index 0000000..a16d650 --- /dev/null +++ b/core/scripts/hysteria2/auth_server.py @@ -0,0 +1,83 @@ +import json +import os +import asyncio +from datetime import datetime, timedelta +from aiohttp import web +import aiofiles +from init_paths import * +from paths import * + +users_data = {} +users_lock = asyncio.Lock() + +async def load_users(app): + global users_data + async with users_lock: + if os.path.exists(USERS_FILE): + async with aiofiles.open(USERS_FILE, 'r') as f: + content = await f.read() + users_data = json.loads(content) + else: + users_data = {} + app['users_data'] = users_data + +async def save_users(): + async with users_lock: + async with aiofiles.open(USERS_FILE, 'w') as f: + await f.write(json.dumps(users_data, indent=4)) + +async def authenticate(request): + global users_data + try: + data = await request.json() + auth_str = data.get("auth") + if not auth_str: + return web.json_response({"ok": False, "msg": "Auth field missing"}, status=400) + + username, password = auth_str.split(":", 1) + except (json.JSONDecodeError, ValueError, TypeError): + return web.json_response({"ok": False, "msg": "Invalid request format"}, status=400) + + async with users_lock: + user = users_data.get(username) + + if not user: + return web.json_response({"ok": False, "msg": "User not found"}, status=401) + + if user.get("blocked", False): + return web.json_response({"ok": False, "msg": "User is blocked"}, status=401) + + if user.get("password") != password: + return web.json_response({"ok": False, "msg": "Invalid password"}, status=401) + + should_save = False + expiration_days = user.get("expiration_days", 0) + if expiration_days > 0: + creation_date_str = user.get("account_creation_date") + if creation_date_str: + creation_date = datetime.strptime(creation_date_str, "%Y-%m-%d") + expiration_date = creation_date + timedelta(days=expiration_days) + if datetime.now() >= expiration_date: + user["blocked"] = True + should_save = True + + max_bytes = user.get("max_download_bytes", 0) + if max_bytes > 0: + current_up = user.get("upload_bytes", 0) + current_down = user.get("download_bytes", 0) + if (current_up + current_down) >= max_bytes: + user["blocked"] = True + should_save = True + + if should_save: + await save_users() + return web.json_response({"ok": False, "msg": "User blocked due to limits"}, status=401) + + return web.json_response({"ok": True, "id": username}) + +app = web.Application() +app.router.add_post("/auth", authenticate) +app.on_startup.append(load_users) + +if __name__ == "__main__": + web.run_app(app, host="127.0.0.1", port=28262) \ No newline at end of file