pia-proxy 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. pia_proxy-1.0.0/LICENSE +21 -0
  2. pia_proxy-1.0.0/MANIFEST.in +28 -0
  3. pia_proxy-1.0.0/PKG-INFO +124 -0
  4. pia_proxy-1.0.0/README.md +97 -0
  5. pia_proxy-1.0.0/cli/__init__.py +5 -0
  6. pia_proxy-1.0.0/cli/api_client.py +128 -0
  7. pia_proxy-1.0.0/cli/commands/__init__.py +3 -0
  8. pia_proxy-1.0.0/cli/commands/config.py +96 -0
  9. pia_proxy-1.0.0/cli/commands/doctor.py +106 -0
  10. pia_proxy-1.0.0/cli/commands/export.py +53 -0
  11. pia_proxy-1.0.0/cli/commands/logs.py +33 -0
  12. pia_proxy-1.0.0/cli/commands/proxy.py +63 -0
  13. pia_proxy-1.0.0/cli/commands/rotate.py +90 -0
  14. pia_proxy-1.0.0/cli/commands/rotate_time.py +71 -0
  15. pia_proxy-1.0.0/cli/commands/scale.py +56 -0
  16. pia_proxy-1.0.0/cli/commands/service.py +136 -0
  17. pia_proxy-1.0.0/cli/commands/setup.py +65 -0
  18. pia_proxy-1.0.0/cli/commands/status.py +182 -0
  19. pia_proxy-1.0.0/cli/commands/test.py +126 -0
  20. pia_proxy-1.0.0/cli/commands/workers.py +66 -0
  21. pia_proxy-1.0.0/cli/config.py +207 -0
  22. pia_proxy-1.0.0/cli/docker_ctl.py +178 -0
  23. pia_proxy-1.0.0/cli/main.py +70 -0
  24. pia_proxy-1.0.0/cli/templates/.env.example +130 -0
  25. pia_proxy-1.0.0/cli/templates/docker-compose.yml +575 -0
  26. pia_proxy-1.0.0/pia_proxy.egg-info/PKG-INFO +124 -0
  27. pia_proxy-1.0.0/pia_proxy.egg-info/SOURCES.txt +31 -0
  28. pia_proxy-1.0.0/pia_proxy.egg-info/dependency_links.txt +1 -0
  29. pia_proxy-1.0.0/pia_proxy.egg-info/entry_points.txt +3 -0
  30. pia_proxy-1.0.0/pia_proxy.egg-info/requires.txt +2 -0
  31. pia_proxy-1.0.0/pia_proxy.egg-info/top_level.txt +1 -0
  32. pia_proxy-1.0.0/pyproject.toml +55 -0
  33. pia_proxy-1.0.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zSOCKS Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,28 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include cli/templates *
5
+
6
+ # Exclude sensitive secrets and runtime files
7
+ prune credentials
8
+ prune prod/credentials
9
+ prune prod
10
+ prune app
11
+ prune gateway
12
+ prune installers
13
+ global-exclude *.pyc
14
+ global-exclude __pycache__
15
+ global-exclude .env
16
+ global-exclude .env*
17
+ include cli/templates/.env.example
18
+ global-exclude *.json
19
+ global-exclude *.log
20
+ global-exclude *.sh
21
+ global-exclude check_*.py
22
+ global-exclude monitor_*.py
23
+ global-exclude launch_*.py
24
+ global-exclude run_*.py
25
+ global-exclude verify_*.py
26
+ global-exclude deploy_*.py
27
+ global-exclude connect_*.py
28
+ global-exclude debug_*.py
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.4
2
+ Name: pia-proxy
3
+ Version: 1.0.0
4
+ Summary: High-Performance PIA SOCKS5 Multi-Worker Proxy Gateway CLI
5
+ Author: zSOCKS Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/videoveo3pro-debug/pia
8
+ Project-URL: Repository, https://github.com/videoveo3pro-debug/pia.git
9
+ Keywords: proxy,socks5,pia,vpn,vpn-gateway,rotate-ip,proxy-pool,haproxy
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Topic :: Internet :: Proxy Servers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Operating System :: POSIX :: Linux
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: click>=8.1.0
25
+ Requires-Dist: rich>=13.0.0
26
+ Dynamic: license-file
27
+
28
+ # PIA SOCKS5 Multi-Worker Proxy Gateway & CLI
29
+
30
+ Hệ thống Proxy SOCKS5 tập trung hiệu năng cao, điều phối **32 Worker VPN Private Internet Access (PIA)** hoạt động song song trong các container biệt lập, đảm bảo **luôn có ít nhất 20 IP công khai độc lập hoạt động đồng thời**, hỗ trợ tự động xoay IP (Auto-Rotate) mượt mà với **100% Uptime (Zero-Downtime)**.
31
+
32
+ ---
33
+
34
+ ## ⚡ Ứng Dụng Quản Lý CLI (`pia-proxy` / `zsocks`)
35
+
36
+ Toàn bộ hệ thống hiện đã được tích hợp bộ công cụ CLI hiện đại, trực quan giúp quản trị, vận hành, kiểm thử và mở rộng pool proxy chỉ bằng các câu lệnh đơn giản:
37
+
38
+ ```bash
39
+ # Cài đặt symlink CLI vào PATH (nếu chưa có):
40
+ chmod +x bin/pia-proxy
41
+ ln -sf $(pwd)/bin/pia-proxy ~/.local/bin/pia-proxy
42
+ ln -sf $(pwd)/bin/pia-proxy ~/.local/bin/zsocks
43
+ ```
44
+
45
+ ### Các lệnh CLI chính:
46
+
47
+ | Lệnh | Chức năng |
48
+ | :--- | :--- |
49
+ | `pia-proxy doctor` | Kiểm tra môi trường hệ thống: Docker, TUN device, Swap, RAM, Port |
50
+ | `pia-proxy config show` | Xem thông tin cấu hình, credentials, ports |
51
+ | `pia-proxy config set-credentials` | Nhập tài khoản PIA (Username / Password) |
52
+ | `pia-proxy config set-proxy-auth` | Đổi thông tin đăng nhập SOCKS5 (`user`, `pass`) |
53
+ | `pia-proxy start` | Khởi động hệ thống thông minh (Staggered Boot chống nghẽn CPU) |
54
+ | `pia-proxy stop` | Dừng an toàn toàn bộ hệ thống hoặc từng worker cụ thể |
55
+ | `pia-proxy restart [-W worker]` | Khởi động lại cụm proxy hoặc một worker chỉ định |
56
+ | `pia-proxy status [-w]` | Xem bảng trạng thái trực tiếp: Exit IP, Uptime, Worker READY, Unique IPs |
57
+ | `pia-proxy scale <N>` | **Mở rộng / thu gọn quy mô động** (ví dụ: `scale 20`, `scale 32`) Zero-Downtime |
58
+ | `pia-proxy rotate [ID] [--all]` | Kích hoạt xoay IP ngay lập tức cho 1 worker hoặc toàn bộ pool |
59
+ | `pia-proxy test [-n 5]` | Kiểm tra kết nối SOCKS5 gateway, đo độ trễ, đếm IP và xác nhận Zero-Leak |
60
+ | `pia-proxy export [-f format]` | **Xuất proxy định dạng sẵn** cho tool nuôi nick, Gologin, AdsPower, bot |
61
+ | `pia-proxy logs [-f]` | Theo dõi log real-time của worker hoặc gateway |
62
+
63
+ ---
64
+
65
+ ## 🚀 Đặc Điểm Nổi Bật
66
+
67
+ - **Cổng SOCKS5 Duy Nhất Cho Mọi Ứng Dụng:**
68
+ `socks5h://proxy_user:proxy_password_secure123@<IP_SERVER>:1087`
69
+ - **Cân Bằng Tải Thông Minh (Round-Robin):**
70
+ Mỗi kết nối / request mới tự động chuyển hướng qua một Worker VPN khác nhau, mang lại dải IP đa dạng và phân bổ đều.
71
+ - **Quy Tắc Xoay Vòng IP (Strict Rotation Rules):**
72
+ - Chu kỳ tự động xoay: **36s – 69s**.
73
+ - Giới hạn cứng Uptime: **70s** (Bất kỳ IP nào đạt 70s sẽ bị ép rotate ngay lập tức).
74
+ - Khống chế dung lượng: Không bao giờ để số worker khả dụng tụt dưới **20 IP**.
75
+ - **Tối Ưu Phần Cứng Tuyệt Đối (Zero CPU Overhead):**
76
+ - Chạy mượt mà ngay cả trên VPS 1 vCPU / 1GB RAM.
77
+ - Sử dụng lightweight TCP healthchecks trên cổng 1080, loại bỏ hoàn toàn hiện tượng 503 flapping và CPU spikes.
78
+ - **Bảo Mật Zero-Leak:**
79
+ - Tường lửa iptables killswitch trong từng worker chặn 100% rò rỉ IP gốc của VPS.
80
+
81
+ ---
82
+
83
+ ## 📖 Ví Dụ Sử Dụng Nhanh
84
+
85
+ ### 1. Xuất chuỗi proxy dùng cho phần mềm:
86
+ ```bash
87
+ # Định dạng chuẩn IP:PORT:USER:PASS (dùng cho antidetect browser, bot, tool)
88
+ pia-proxy export --format standard
89
+ # Kết quả: 14.169.55.237:1087:proxy_user:proxy_password_secure123
90
+
91
+ # Định dạng URL:
92
+ pia-proxy export --format url
93
+ # Kết quả: socks5h://proxy_user:proxy_password_secure123@14.169.55.237:1087
94
+ ```
95
+
96
+ ### 2. Xem bảng trạng thái trực quan:
97
+ ```bash
98
+ pia-proxy status
99
+ # Giám sát trực tiếp liên tục mỗi 2 giây:
100
+ pia-proxy status --watch -i 2
101
+ ```
102
+
103
+ ### 3. Mở rộng số lượng worker khi cần:
104
+ ```bash
105
+ # Tăng lên 32 worker:
106
+ pia-proxy scale 32
107
+
108
+ # Giảm xuống 20 worker:
109
+ pia-proxy scale 20
110
+ ```
111
+
112
+ ### 4. Kiểm thử chất lượng proxy & Zero-leak:
113
+ ```bash
114
+ pia-proxy test --count 5
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 🛠️ Triển Khai Thủ Công & Link Trực Tiếp
120
+
121
+ - **Web Dashboard:** `http://<IP_SERVER>:8007/`
122
+ - **API Status:** `http://<IP_SERVER>:8007/api/status`
123
+ - **HAProxy Live Stats:** `http://<IP_SERVER>:8411/`
124
+ - **Hướng dẫn cấu hình nâng cao:** [prod/README.md](prod/README.md)
@@ -0,0 +1,97 @@
1
+ # PIA SOCKS5 Multi-Worker Proxy Gateway & CLI
2
+
3
+ Hệ thống Proxy SOCKS5 tập trung hiệu năng cao, điều phối **32 Worker VPN Private Internet Access (PIA)** hoạt động song song trong các container biệt lập, đảm bảo **luôn có ít nhất 20 IP công khai độc lập hoạt động đồng thời**, hỗ trợ tự động xoay IP (Auto-Rotate) mượt mà với **100% Uptime (Zero-Downtime)**.
4
+
5
+ ---
6
+
7
+ ## ⚡ Ứng Dụng Quản Lý CLI (`pia-proxy` / `zsocks`)
8
+
9
+ Toàn bộ hệ thống hiện đã được tích hợp bộ công cụ CLI hiện đại, trực quan giúp quản trị, vận hành, kiểm thử và mở rộng pool proxy chỉ bằng các câu lệnh đơn giản:
10
+
11
+ ```bash
12
+ # Cài đặt symlink CLI vào PATH (nếu chưa có):
13
+ chmod +x bin/pia-proxy
14
+ ln -sf $(pwd)/bin/pia-proxy ~/.local/bin/pia-proxy
15
+ ln -sf $(pwd)/bin/pia-proxy ~/.local/bin/zsocks
16
+ ```
17
+
18
+ ### Các lệnh CLI chính:
19
+
20
+ | Lệnh | Chức năng |
21
+ | :--- | :--- |
22
+ | `pia-proxy doctor` | Kiểm tra môi trường hệ thống: Docker, TUN device, Swap, RAM, Port |
23
+ | `pia-proxy config show` | Xem thông tin cấu hình, credentials, ports |
24
+ | `pia-proxy config set-credentials` | Nhập tài khoản PIA (Username / Password) |
25
+ | `pia-proxy config set-proxy-auth` | Đổi thông tin đăng nhập SOCKS5 (`user`, `pass`) |
26
+ | `pia-proxy start` | Khởi động hệ thống thông minh (Staggered Boot chống nghẽn CPU) |
27
+ | `pia-proxy stop` | Dừng an toàn toàn bộ hệ thống hoặc từng worker cụ thể |
28
+ | `pia-proxy restart [-W worker]` | Khởi động lại cụm proxy hoặc một worker chỉ định |
29
+ | `pia-proxy status [-w]` | Xem bảng trạng thái trực tiếp: Exit IP, Uptime, Worker READY, Unique IPs |
30
+ | `pia-proxy scale <N>` | **Mở rộng / thu gọn quy mô động** (ví dụ: `scale 20`, `scale 32`) Zero-Downtime |
31
+ | `pia-proxy rotate [ID] [--all]` | Kích hoạt xoay IP ngay lập tức cho 1 worker hoặc toàn bộ pool |
32
+ | `pia-proxy test [-n 5]` | Kiểm tra kết nối SOCKS5 gateway, đo độ trễ, đếm IP và xác nhận Zero-Leak |
33
+ | `pia-proxy export [-f format]` | **Xuất proxy định dạng sẵn** cho tool nuôi nick, Gologin, AdsPower, bot |
34
+ | `pia-proxy logs [-f]` | Theo dõi log real-time của worker hoặc gateway |
35
+
36
+ ---
37
+
38
+ ## 🚀 Đặc Điểm Nổi Bật
39
+
40
+ - **Cổng SOCKS5 Duy Nhất Cho Mọi Ứng Dụng:**
41
+ `socks5h://proxy_user:proxy_password_secure123@<IP_SERVER>:1087`
42
+ - **Cân Bằng Tải Thông Minh (Round-Robin):**
43
+ Mỗi kết nối / request mới tự động chuyển hướng qua một Worker VPN khác nhau, mang lại dải IP đa dạng và phân bổ đều.
44
+ - **Quy Tắc Xoay Vòng IP (Strict Rotation Rules):**
45
+ - Chu kỳ tự động xoay: **36s – 69s**.
46
+ - Giới hạn cứng Uptime: **70s** (Bất kỳ IP nào đạt 70s sẽ bị ép rotate ngay lập tức).
47
+ - Khống chế dung lượng: Không bao giờ để số worker khả dụng tụt dưới **20 IP**.
48
+ - **Tối Ưu Phần Cứng Tuyệt Đối (Zero CPU Overhead):**
49
+ - Chạy mượt mà ngay cả trên VPS 1 vCPU / 1GB RAM.
50
+ - Sử dụng lightweight TCP healthchecks trên cổng 1080, loại bỏ hoàn toàn hiện tượng 503 flapping và CPU spikes.
51
+ - **Bảo Mật Zero-Leak:**
52
+ - Tường lửa iptables killswitch trong từng worker chặn 100% rò rỉ IP gốc của VPS.
53
+
54
+ ---
55
+
56
+ ## 📖 Ví Dụ Sử Dụng Nhanh
57
+
58
+ ### 1. Xuất chuỗi proxy dùng cho phần mềm:
59
+ ```bash
60
+ # Định dạng chuẩn IP:PORT:USER:PASS (dùng cho antidetect browser, bot, tool)
61
+ pia-proxy export --format standard
62
+ # Kết quả: 14.169.55.237:1087:proxy_user:proxy_password_secure123
63
+
64
+ # Định dạng URL:
65
+ pia-proxy export --format url
66
+ # Kết quả: socks5h://proxy_user:proxy_password_secure123@14.169.55.237:1087
67
+ ```
68
+
69
+ ### 2. Xem bảng trạng thái trực quan:
70
+ ```bash
71
+ pia-proxy status
72
+ # Giám sát trực tiếp liên tục mỗi 2 giây:
73
+ pia-proxy status --watch -i 2
74
+ ```
75
+
76
+ ### 3. Mở rộng số lượng worker khi cần:
77
+ ```bash
78
+ # Tăng lên 32 worker:
79
+ pia-proxy scale 32
80
+
81
+ # Giảm xuống 20 worker:
82
+ pia-proxy scale 20
83
+ ```
84
+
85
+ ### 4. Kiểm thử chất lượng proxy & Zero-leak:
86
+ ```bash
87
+ pia-proxy test --count 5
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 🛠️ Triển Khai Thủ Công & Link Trực Tiếp
93
+
94
+ - **Web Dashboard:** `http://<IP_SERVER>:8007/`
95
+ - **API Status:** `http://<IP_SERVER>:8007/api/status`
96
+ - **HAProxy Live Stats:** `http://<IP_SERVER>:8411/`
97
+ - **Hướng dẫn cấu hình nâng cao:** [prod/README.md](prod/README.md)
@@ -0,0 +1,5 @@
1
+ """
2
+ PIA SOCKS5 Multi-Worker Proxy Pool - CLI Management Suite
3
+ """
4
+
5
+ __version__ = "1.0.0"
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import urllib.error
5
+ import urllib.parse
6
+ import urllib.request
7
+ from typing import Any
8
+
9
+
10
+ class APIClientError(Exception):
11
+ def __init__(self, message: str, status_code: int | None = None, response_body: Any = None):
12
+ super().__init__(message)
13
+ self.status_code = status_code
14
+ self.response_body = response_body
15
+
16
+
17
+ class APIClient:
18
+ def __init__(self, base_url: str, token: str = "", timeout: float = 10.0):
19
+ self.base_url = base_url.rstrip("/")
20
+ self.token = token.strip()
21
+ self.timeout = timeout
22
+
23
+ def _request(
24
+ self,
25
+ method: str,
26
+ path: str,
27
+ params: dict[str, Any] | None = None,
28
+ json_data: dict[str, Any] | None = None,
29
+ timeout: float | None = None,
30
+ ) -> dict[str, Any]:
31
+ url = f"{self.base_url}{path}"
32
+ if params:
33
+ query = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None})
34
+ if query:
35
+ url = f"{url}?{query}"
36
+
37
+ headers = {
38
+ "User-Agent": "pia-proxy-cli/1.0",
39
+ "Accept": "application/json",
40
+ }
41
+ if self.token:
42
+ headers["Authorization"] = f"Bearer {self.token}"
43
+
44
+ data_bytes = None
45
+ if json_data is not None:
46
+ data_bytes = json.dumps(json_data).encode("utf-8")
47
+ headers["Content-Type"] = "application/json"
48
+
49
+ req = urllib.request.Request(url, data=data_bytes, headers=headers, method=method.upper())
50
+
51
+ try:
52
+ with urllib.request.urlopen(req, timeout=timeout or self.timeout) as resp:
53
+ raw = resp.read().decode("utf-8")
54
+ if not raw.strip():
55
+ return {}
56
+ return json.loads(raw)
57
+ except urllib.error.HTTPError as e:
58
+ raw_err = e.read().decode("utf-8", errors="ignore")
59
+ try:
60
+ body = json.loads(raw_err)
61
+ except Exception:
62
+ body = raw_err
63
+ raise APIClientError(
64
+ message=f"HTTP {e.code}: {e.reason}",
65
+ status_code=e.code,
66
+ response_body=body,
67
+ ) from e
68
+ except urllib.error.URLError as e:
69
+ raise APIClientError(message=f"Connection failed: {e.reason}") from e
70
+ except Exception as e:
71
+ raise APIClientError(message=f"Request error: {str(e)}") from e
72
+
73
+ def is_alive(self) -> bool:
74
+ try:
75
+ res = self._request("GET", "/healthz", timeout=3.0)
76
+ return bool(res.get("ok"))
77
+ except Exception:
78
+ return False
79
+
80
+ def get_status(self, refresh: bool = False) -> dict[str, Any]:
81
+ return self._request("GET", "/api/status", params={"refresh": refresh}, timeout=12.0)
82
+
83
+ def get_nodes(self, refresh: bool = False) -> list[dict[str, Any]]:
84
+ res = self._request("GET", "/api/nodes", params={"refresh": refresh}, timeout=10.0)
85
+ return res.get("nodes", [])
86
+
87
+ def get_healthy_nodes(self) -> dict[str, Any]:
88
+ return self._request("GET", "/api/nodes/healthy", timeout=6.0)
89
+
90
+ def get_node(self, node_id: str) -> dict[str, Any]:
91
+ return self._request("GET", f"/api/nodes/{node_id}", timeout=6.0)
92
+
93
+ def set_mode(self, worker_count: int) -> dict[str, Any]:
94
+ return self._request("POST", "/api/mode", json_data={"worker_count": worker_count}, timeout=30.0)
95
+
96
+ def get_mode(self) -> dict[str, Any]:
97
+ return self._request("GET", "/api/mode", timeout=5.0)
98
+
99
+ def rotate_any(self, country: str | None = None, server: str | None = None, wait_for_ready: bool = False) -> dict[str, Any]:
100
+ payload: dict[str, Any] = {"wait_for_ready": wait_for_ready}
101
+ if country:
102
+ payload["country"] = country
103
+ if server:
104
+ payload["server"] = server
105
+ return self._request("POST", "/api/rotate-any", json_data=payload, timeout=20.0)
106
+
107
+ def rotate_node(self, node_id: str, country: str | None = None, server: str | None = None, wait_for_ready: bool = False) -> dict[str, Any]:
108
+ payload: dict[str, Any] = {"wait_for_ready": wait_for_ready}
109
+ if country:
110
+ payload["country"] = country
111
+ if server:
112
+ payload["server"] = server
113
+ return self._request("POST", f"/api/nodes/{node_id}/rotate", json_data=payload, timeout=25.0)
114
+
115
+ def recover_all(self, force: bool = False) -> dict[str, Any]:
116
+ return self._request("POST", "/api/recover", json_data={"force": force}, timeout=15.0)
117
+
118
+ def recover_node(self, node_id: str, force: bool = False) -> dict[str, Any]:
119
+ return self._request("POST", f"/api/nodes/{node_id}/recover", json_data={"force": force}, timeout=15.0)
120
+
121
+ def test_proxy(self) -> dict[str, Any]:
122
+ return self._request("GET", "/api/test-proxy", timeout=15.0)
123
+
124
+ def get_countries(self, refresh: bool = False) -> dict[str, Any]:
125
+ return self._request("GET", "/api/countries", params={"refresh": refresh}, timeout=15.0)
126
+
127
+ def get_logs(self, tail: int = 100) -> dict[str, Any]:
128
+ return self._request("GET", "/api/logs", params={"tail": tail}, timeout=10.0)
@@ -0,0 +1,3 @@
1
+ """
2
+ CLI Subcommands package
3
+ """
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import click
6
+ from rich.console import Console
7
+ from rich.table import Table
8
+
9
+ from cli.config import CLIConfig
10
+
11
+
12
+ @click.group("config", help="Manage proxy gateway credentials, environment settings, and ports.")
13
+ def config_group() -> None:
14
+ pass
15
+
16
+
17
+ @config_group.command("show", help="Display all active configuration parameters.")
18
+ @click.pass_obj
19
+ def config_show(obj: dict[str, Any]) -> None:
20
+ config: CLIConfig = obj["config"]
21
+ console = Console()
22
+
23
+ table = Table(title="[bold cyan]PIA Proxy Configuration Summary[/bold cyan]", expand=True)
24
+ table.add_column("Parameter", style="bold white", width=24)
25
+ table.add_column("Value", style="yellow")
26
+ table.add_column("Location / Source", style="dim")
27
+
28
+ table.add_row("Project Root", str(config.project_root), "Detected")
29
+ table.add_row("Compose Dir", str(config.compose_dir), "Detected")
30
+ table.add_row(".env File", str(config.env_file), "Detected")
31
+ table.add_row("Credentials Dir", str(config.credentials_dir), "Detected")
32
+ table.add_row("SOCKS5 Proxy Port", str(config.proxy_port), ".env (PROXY_PORT)")
33
+ table.add_row("SOCKS5 Username", config.socks5_user, ".env (SOCKS5_USERNAME)")
34
+ table.add_row("SOCKS5 Password", "***" + config.socks5_pass[-4:] if len(config.socks5_pass) > 4 else "***", ".env (SOCKS5_PASSWORD)")
35
+ table.add_row("Backend API Port", str(config.api_port), ".env (API_PORT)")
36
+ table.add_row("Active Worker Count", str(config.worker_count), ".env (WORKER_COUNT)")
37
+ table.add_row("Max Worker Count", str(config.max_worker_count), ".env (MAX_WORKER_COUNT)")
38
+
39
+ u, p = config.get_pia_account()
40
+ if u and p:
41
+ table.add_row("PIA Account", f"{u} / {'*' * len(p)}", "credentials/pia_account_1")
42
+ else:
43
+ table.add_row("PIA Account", "[bold red]Not Configured![/bold red]", "credentials/pia_account_1")
44
+
45
+ session_file = config.credentials_dir / "pia_session_slot_1.json"
46
+ table.add_row("PIA Session Cache", "Present" if session_file.exists() else "Missing", "credentials/pia_session_slot_1.json")
47
+
48
+ console.print(table)
49
+
50
+
51
+ @config_group.command("set-credentials", help="Configure PIA account username and password.")
52
+ @click.option("--username", "-u", prompt=True, help="PIA Username (e.g. p1234567)")
53
+ @click.option("--password", "-p", prompt=True, hide_input=True, confirmation_prompt=True, help="PIA Password")
54
+ @click.pass_obj
55
+ def config_set_credentials(obj: dict[str, Any], username: str, password: str) -> None:
56
+ config: CLIConfig = obj["config"]
57
+ console = Console()
58
+
59
+ if not username.strip() or not password.strip():
60
+ console.print("[bold red]Username and password cannot be empty.[/bold red]")
61
+ raise SystemExit(1)
62
+
63
+ config.set_pia_account(username.strip(), password.strip())
64
+ console.print(f"[bold green]✔ Saved PIA account credentials to {config.credentials_dir / 'pia_account_1'}.[/bold green]")
65
+
66
+
67
+ @config_group.command("set-proxy-auth", help="Set username and password required for SOCKS5 proxy clients.")
68
+ @click.option("--username", "-u", prompt=True, help="Proxy username")
69
+ @click.option("--password", "-p", prompt=True, hide_input=True, confirmation_prompt=True, help="Proxy password")
70
+ @click.pass_obj
71
+ def config_set_proxy_auth(obj: dict[str, Any], username: str, password: str) -> None:
72
+ config: CLIConfig = obj["config"]
73
+ console = Console()
74
+
75
+ config.update_env_value("SOCKS5_USERNAME", username.strip())
76
+ config.update_env_value("SOCKS5_PASSWORD", password.strip())
77
+ console.print("[bold green]✔ Updated SOCKS5 credentials in .env.[/bold green]")
78
+ console.print("[dim]Note: Restart proxy pool with 'pia-proxy restart' to apply new proxy credentials.[/dim]")
79
+
80
+
81
+ @config_group.command("set-port", help="Set the external port for SOCKS5 gateway or backend API.")
82
+ @click.option("--proxy-port", type=int, default=None, help="New SOCKS5 gateway port (e.g. 1087, 1080)")
83
+ @click.option("--api-port", type=int, default=None, help="New Backend API port (e.g. 8007)")
84
+ @click.pass_obj
85
+ def config_set_port(obj: dict[str, Any], proxy_port: int | None, api_port: int | None) -> None:
86
+ config: CLIConfig = obj["config"]
87
+ console = Console()
88
+
89
+ if proxy_port:
90
+ config.update_env_value("PROXY_PORT", proxy_port)
91
+ console.print(f"[bold green]✔ Set PROXY_PORT={proxy_port} in .env.[/bold green]")
92
+ if api_port:
93
+ config.update_env_value("API_PORT", api_port)
94
+ console.print(f"[bold green]✔ Set API_PORT={api_port} in .env.[/bold green]")
95
+
96
+ console.print("[dim]Note: Restart proxy pool with 'pia-proxy restart' to apply port changes.[/dim]")
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import socket
5
+ import subprocess
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import click
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+ from rich.text import Text
13
+
14
+ from cli.config import CLIConfig
15
+
16
+
17
+ def _is_port_open(port: int, host: str = "127.0.0.1") -> bool:
18
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
19
+ s.settimeout(0.5)
20
+ return s.connect_ex((host, port)) == 0
21
+
22
+
23
+ @click.command("doctor", help="Inspect system readiness, tun device, ports, swap, and Docker requirements.")
24
+ @click.pass_obj
25
+ def doctor_cmd(obj: dict[str, Any]) -> None:
26
+ config: CLIConfig = obj["config"]
27
+ console = Console()
28
+
29
+ table = Table(title="[bold cyan]System Environment & Health Diagnostics[/bold cyan]", expand=True)
30
+ table.add_column("Component", style="bold white", width=22)
31
+ table.add_column("Status", width=16)
32
+ table.add_column("Details", style="dim")
33
+
34
+ # 1. Docker Engine
35
+ docker_ok = False
36
+ try:
37
+ r = subprocess.run(["docker", "info"], capture_output=True, text=True, timeout=3)
38
+ docker_ok = r.returncode == 0
39
+ except Exception:
40
+ pass
41
+
42
+ if docker_ok:
43
+ table.add_row("Docker Engine", Text("✔ OK", style="bold green"), "Daemon is active and reachable")
44
+ else:
45
+ table.add_row("Docker Engine", Text("✗ ERROR", style="bold red"), "Docker daemon not running or access denied")
46
+
47
+ # 2. Docker Compose
48
+ compose_ok = False
49
+ try:
50
+ r = subprocess.run(["docker", "compose", "version"], capture_output=True, text=True, timeout=3)
51
+ compose_ok = r.returncode == 0
52
+ compose_ver = r.stdout.strip()
53
+ except Exception:
54
+ compose_ver = "Not installed"
55
+
56
+ if compose_ok:
57
+ table.add_row("Docker Compose", Text("✔ OK", style="bold green"), compose_ver)
58
+ else:
59
+ table.add_row("Docker Compose", Text("✗ ERROR", style="bold red"), "docker compose command missing")
60
+
61
+ # 3. TUN device
62
+ tun_path = Path("/dev/net/tun")
63
+ if tun_path.exists():
64
+ table.add_row("TUN Device", Text("✔ OK", style="bold green"), "/dev/net/tun is present")
65
+ else:
66
+ table.add_row("TUN Device", Text("✗ MISSING", style="bold red"), "Run 'mkdir -p /dev/net && mknod /dev/net/tun c 10 200'")
67
+
68
+ # 4. RAM & Swap
69
+ mem_ok = False
70
+ mem_info = "Unable to read /proc/meminfo"
71
+ try:
72
+ with open("/proc/meminfo") as f:
73
+ lines = f.readlines()
74
+ total_ram = 0
75
+ total_swap = 0
76
+ for line in lines:
77
+ if line.startswith("MemTotal:"):
78
+ total_ram = int(line.split()[1]) // 1024
79
+ elif line.startswith("SwapTotal:"):
80
+ total_swap = int(line.split()[1]) // 1024
81
+ mem_info = f"RAM: {total_ram}MB | Swap: {total_swap}MB"
82
+ mem_ok = total_swap >= 1000 or total_ram >= 3000
83
+ except Exception:
84
+ pass
85
+
86
+ if mem_ok:
87
+ table.add_row("Memory & Swap", Text("✔ OK", style="bold green"), mem_info)
88
+ else:
89
+ table.add_row("Memory & Swap", Text("⚠ WARN", style="bold yellow"), f"{mem_info} (Recommend adding >= 2GB swap)")
90
+
91
+ # 5. PIA Credentials
92
+ u, p = config.get_pia_account()
93
+ if u and p:
94
+ table.add_row("PIA Account", Text("✔ READY", style="bold green"), f"Username: {u}")
95
+ else:
96
+ table.add_row("PIA Account", Text("✗ NOT SET", style="bold red"), "Configure with 'pia-proxy config set-credentials'")
97
+
98
+ # 6. Gateway Port (1087)
99
+ gw_in_use = _is_port_open(config.proxy_port)
100
+ table.add_row(f"Proxy Port ({config.proxy_port})", Text("IN USE" if gw_in_use else "AVAILABLE", style="cyan" if gw_in_use else "green"), "SOCKS5 Proxy Gateway")
101
+
102
+ # 7. API Port (8007)
103
+ api_in_use = _is_port_open(config.api_port)
104
+ table.add_row(f"API Port ({config.api_port})", Text("IN USE" if api_in_use else "AVAILABLE", style="cyan" if api_in_use else "green"), "FastAPI Backend")
105
+
106
+ console.print(table)
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ import click
7
+ from rich.console import Console
8
+
9
+ from cli.config import CLIConfig
10
+
11
+
12
+ @click.command("export", help="Export proxy connection strings formatted for bots, antidetect browsers, or crawlers.")
13
+ @click.option(
14
+ "--format",
15
+ "-f",
16
+ type=click.Choice(["url", "standard", "curl", "json", "env"]),
17
+ default="standard",
18
+ help="Output format (standard = IP:PORT:USER:PASS).",
19
+ show_default=True,
20
+ )
21
+ @click.option("--host", "-H", default=None, help="Custom server IP or hostname override.")
22
+ @click.pass_obj
23
+ def export_cmd(obj: dict[str, Any], format: str, host: str | None) -> None:
24
+ config: CLIConfig = obj["config"]
25
+ console = Console()
26
+
27
+ server_ip = host or config.get_public_ip()
28
+ port = config.proxy_port
29
+ user = config.socks5_user
30
+ pwd = config.socks5_pass
31
+
32
+ if format == "standard":
33
+ # IP:PORT:USER:PASS format commonly accepted by antidetect tools
34
+ click.echo(f"{server_ip}:{port}:{user}:{pwd}")
35
+ elif format == "url":
36
+ click.echo(f"socks5h://{user}:{pwd}@{server_ip}:{port}")
37
+ elif format == "curl":
38
+ click.echo(f"curl -x socks5h://{user}:{pwd}@{server_ip}:{port} https://api.ipify.org")
39
+ elif format == "json":
40
+ data = {
41
+ "type": "socks5",
42
+ "host": server_ip,
43
+ "port": port,
44
+ "username": user,
45
+ "password": pwd,
46
+ "url": f"socks5h://{user}:{pwd}@{server_ip}:{port}",
47
+ "standard": f"{server_ip}:{port}:{user}:{pwd}",
48
+ }
49
+ click.echo(json.dumps(data, indent=2))
50
+ elif format == "env":
51
+ click.echo(f'export ALL_PROXY="socks5h://{user}:{pwd}@{server_ip}:{port}"')
52
+ click.echo(f'export HTTP_PROXY="socks5h://{user}:{pwd}@{server_ip}:{port}"')
53
+ click.echo(f'export HTTPS_PROXY="socks5h://{user}:{pwd}@{server_ip}:{port}"')