mirror of
https://github.com/Flowseal/tg-ws-proxy.git
synced 2026-06-21 22:11:09 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5bc5001c4d | |||
| 2afd80825b |
+1
-1
@@ -49,7 +49,7 @@
|
|||||||
- [Fake TLS + upstream в Nginx](./FakeTlsNginx.md)
|
- [Fake TLS + upstream в Nginx](./FakeTlsNginx.md)
|
||||||
- [Файлы конфигурации Tray-приложения](./TrayConfig.md)
|
- [Файлы конфигурации Tray-приложения](./TrayConfig.md)
|
||||||
- [Установка из исходников](./BuildFromSource.md)
|
- [Установка из исходников](./BuildFromSource.md)
|
||||||
- [Руководство для контрибьюторов](../CONTRIBUTING.md)
|
- [Руководство для контрибьюторов](./CONTRIBUTING.md)
|
||||||
|
|
||||||
## Windows: быстрый вход
|
## Windows: быстрый вход
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,10 @@
|
|||||||
- **Порт:** `1443` (или переопределенный вами)
|
- **Порт:** `1443` (или переопределенный вами)
|
||||||
- **Secret:** из настроек или логов
|
- **Secret:** из настроек или логов
|
||||||
|
|
||||||
|
## Портативный режим
|
||||||
|
Портативный режим автоматически включается, если рядом с исполняемым файлом есть папка с названием `TgWsProxy_data`.
|
||||||
|
Либо можно принудительно включить портативный режим (который сам создаст папку), запустив исполняемый файл с параметром `--portable`.
|
||||||
|
|
||||||
## Установка из исходников
|
## Установка из исходников
|
||||||
|
|
||||||
Подробная инструкция: [BuildFromSource.md](./BuildFromSource.md)
|
Подробная инструкция: [BuildFromSource.md](./BuildFromSource.md)
|
||||||
|
|||||||
+80
-36
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Минимальная проверка новой версии через GitHub Releases API (без сторонних зависимостей).
|
Проверка новой версии через GitHub Releases API
|
||||||
|
|
||||||
Ограничение частоты запросов: не чаще одного раза в час на машину (кэш в каталоге
|
Ограничение частоты запросов: не чаще одного раза в час на машину (кэш в каталоге
|
||||||
данных приложения). Поддерживается If-None-Match (ETag) для ответа 304.
|
данных приложения). Поддерживается If-None-Match (ETag) для ответа 304.
|
||||||
@@ -18,6 +18,7 @@ from proxy.utils import build_github_opener
|
|||||||
|
|
||||||
REPO = "Flowseal/tg-ws-proxy"
|
REPO = "Flowseal/tg-ws-proxy"
|
||||||
RELEASES_LATEST_API = f"https://api.github.com/repos/{REPO}/releases/latest"
|
RELEASES_LATEST_API = f"https://api.github.com/repos/{REPO}/releases/latest"
|
||||||
|
RELEASES_BY_TAG_API = f"https://api.github.com/repos/{REPO}/releases/tags/{{tag}}?t={{timestamp}}"
|
||||||
RELEASES_PAGE_URL = f"https://github.com/{REPO}/releases/latest"
|
RELEASES_PAGE_URL = f"https://github.com/{REPO}/releases/latest"
|
||||||
|
|
||||||
# Не чаще одного полного запроса к API в час (без учёта 304 с тем же ETag).
|
# Не чаще одного полного запроса к API в час (без учёта 304 с тем же ETag).
|
||||||
@@ -223,58 +224,101 @@ def run_check(current_version: str) -> None:
|
|||||||
_state["html_url"] = RELEASES_PAGE_URL
|
_state["html_url"] = RELEASES_PAGE_URL
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_release_by_tag(
|
||||||
|
tag: str, timeout: float = 12.0,
|
||||||
|
) -> Tuple[Optional[dict], int]:
|
||||||
|
if not tag:
|
||||||
|
return None, 0
|
||||||
|
headers = {
|
||||||
|
"Accept": "application/vnd.github+json",
|
||||||
|
"User-Agent": "tg-ws-proxy-update-check",
|
||||||
|
}
|
||||||
|
req = Request(
|
||||||
|
RELEASES_BY_TAG_API.format(tag=tag, timestamp=int(time.time())),
|
||||||
|
headers=headers,
|
||||||
|
method="GET",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with build_github_opener().open(req, timeout=timeout) as resp:
|
||||||
|
code = getattr(resp, "status", None) or resp.getcode()
|
||||||
|
raw = resp.read().decode("utf-8", errors="replace")
|
||||||
|
return json.loads(raw), int(code)
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.code in [304, 404]:
|
||||||
|
return None, e.code
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_assets(data: Optional[dict]) -> list:
|
||||||
|
if not data:
|
||||||
|
return []
|
||||||
|
return [
|
||||||
|
{"name": a.get("name", ""), "url": a.get("browser_download_url", ""), "digest": a.get("digest", "")}
|
||||||
|
for a in (data.get("assets") or [])
|
||||||
|
if a.get("name") and a.get("browser_download_url")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_status() -> Dict[str, Any]:
|
def get_status() -> Dict[str, Any]:
|
||||||
"""Снимок состояния после run_check (для подписей в настройках)."""
|
"""Снимок состояния после run_check (для подписей в настройках)."""
|
||||||
return dict(_state)
|
return dict(_state)
|
||||||
|
|
||||||
|
|
||||||
def get_update_asset(exe_path: Path) -> Optional[Tuple[str, str]]:
|
def get_update_asset(exe_path: Path, current_version: str) -> Optional[Tuple[str, str]]:
|
||||||
assets = _state.get("assets") or []
|
new_assets = _state.get("assets") or []
|
||||||
if not assets:
|
if not new_assets:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Try SHA256 match against release asset digests
|
target_name = None
|
||||||
|
|
||||||
|
# SHA256 match
|
||||||
try:
|
try:
|
||||||
import hashlib
|
import hashlib
|
||||||
h = hashlib.sha256()
|
data, code = fetch_release_by_tag(f"v{current_version}")
|
||||||
with open(exe_path, "rb") as f:
|
if code == 200 and data:
|
||||||
while True:
|
cur_assets = _extract_assets(data)
|
||||||
chunk = f.read(65536)
|
if cur_assets:
|
||||||
if not chunk:
|
h = hashlib.sha256()
|
||||||
break
|
with open(exe_path, "rb") as f:
|
||||||
h.update(chunk)
|
while True:
|
||||||
exe_sha = h.hexdigest().lower()
|
chunk = f.read(65536)
|
||||||
for a in assets:
|
if not chunk:
|
||||||
d = (a.get("digest") or "").lower()
|
break
|
||||||
if d.startswith("sha256:") and d[7:] == exe_sha:
|
h.update(chunk)
|
||||||
return a["url"], a["name"]
|
exe_sha = h.hexdigest().lower()
|
||||||
|
for a in cur_assets:
|
||||||
|
d = (a.get("digest") or "").lower()
|
||||||
|
if d.startswith("sha256:") and d[7:] == exe_sha:
|
||||||
|
target_name = a["name"]
|
||||||
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Fallback
|
# Fallback
|
||||||
import platform
|
if not target_name or target_name not in [a.get("name") for a in new_assets]:
|
||||||
import struct
|
import platform
|
||||||
|
import struct
|
||||||
|
|
||||||
is_64 = struct.calcsize("P") * 8 == 64
|
is_64 = struct.calcsize("P") * 8 == 64
|
||||||
machine = platform.machine().lower()
|
machine = platform.machine().lower()
|
||||||
is_arm64 = machine in ("arm64", "aarch64")
|
is_arm64 = machine in ("arm64", "aarch64")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
is_modern = sys.getwindowsversion().major >= 10
|
is_modern = sys.getwindowsversion().major >= 10
|
||||||
except Exception:
|
except Exception:
|
||||||
is_modern = True
|
is_modern = True
|
||||||
|
|
||||||
if is_arm64:
|
if is_arm64:
|
||||||
name = "TgWsProxy_windows_arm64.exe"
|
target_name = "TgWsProxy_windows_arm64.exe"
|
||||||
elif is_modern:
|
elif is_modern:
|
||||||
name = "TgWsProxy_windows.exe"
|
target_name = "TgWsProxy_windows.exe"
|
||||||
elif is_64:
|
elif is_64:
|
||||||
name = "TgWsProxy_windows_7_64bit.exe"
|
target_name = "TgWsProxy_windows_7_64bit.exe"
|
||||||
else:
|
else:
|
||||||
name = "TgWsProxy_windows_7_32bit.exe"
|
target_name = "TgWsProxy_windows_7_32bit.exe"
|
||||||
|
|
||||||
for a in assets:
|
for a in new_assets:
|
||||||
if a.get("name") == name:
|
if a.get("name") == target_name:
|
||||||
return a["url"], a["name"]
|
return a["url"], a["name"]
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|||||||
+1
-1
@@ -333,7 +333,7 @@ def _maybe_do_update(cfg: dict, is_exiting) -> None:
|
|||||||
return
|
return
|
||||||
url = (st.get("html_url") or "").strip() or RELEASES_PAGE_URL
|
url = (st.get("html_url") or "").strip() or RELEASES_PAGE_URL
|
||||||
ver = st.get("latest") or "?"
|
ver = st.get("latest") or "?"
|
||||||
asset = get_update_asset(Path(sys.executable)) if IS_FROZEN else None
|
asset = get_update_asset(Path(sys.executable), __version__) if IS_FROZEN else None
|
||||||
choice = update_ctk_form(
|
choice = update_ctk_form(
|
||||||
f"Доступна новая версия: {ver}",
|
f"Доступна новая версия: {ver}",
|
||||||
download_url=asset[0] if asset else None,
|
download_url=asset[0] if asset else None,
|
||||||
|
|||||||
Reference in New Issue
Block a user