Template
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6206697e3b | ||
|
|
4c19099b04 | ||
|
|
80160d0ef4 | ||
|
|
ac76a0e540 | ||
|
|
bf7bd16fcd | ||
|
|
24e793d943 | ||
|
|
415cfea5eb |
@@ -63,7 +63,7 @@ Configuration panel for system parameters and preferences:
|
|||||||
* **Classic WireGuard**: Standard, high-performance WireGuard protocol for unmatched speed and broad device compatibility with traffic monitoring support.
|
* **Classic WireGuard**: Standard, high-performance WireGuard protocol for unmatched speed and broad device compatibility with traffic monitoring support.
|
||||||
* **Xray (XTLS-Reality)**: Stealthy protocol that masks VPN traffic as standard HTTPS browsing. Pinned to **Xray-core v26.x**; transparently reads both the **panel layout** (`meta.json` + `clientsTable.json`) and the **native Amnezia client layout** (`xray_*.key` files + `clientsTable`), so a node first installed via the official mobile/desktop app can be attached to the panel without re-installation.
|
* **Xray (XTLS-Reality)**: Stealthy protocol that masks VPN traffic as standard HTTPS browsing. Pinned to **Xray-core v26.x**; transparently reads both the **panel layout** (`meta.json` + `clientsTable.json`) and the **native Amnezia client layout** (`xray_*.key` files + `clientsTable`), so a node first installed via the official mobile/desktop app can be attached to the panel without re-installation.
|
||||||
* **Hysteria 2**: QUIC/HTTP3 proxy from [apernet/hysteria](https://github.com/apernet/hysteria) via `tobyxdd/hysteria:v2` — Let's Encrypt TLS, salamander obfuscation, password auth, `hy2://` share links. Per-server SSL domain/email defaults, renew from settings; install requires free TCP **80** and **443**.
|
* **Hysteria 2**: QUIC/HTTP3 proxy from [apernet/hysteria](https://github.com/apernet/hysteria) via `tobyxdd/hysteria:v2` — Let's Encrypt TLS, salamander obfuscation, password auth, `hy2://` share links. Per-server SSL domain/email defaults, renew from settings; install requires free TCP **80** and **443**.
|
||||||
* **NaiveProxy**: HTTPS/HTTP2 camouflage proxy via [klzgrad/naiveproxy](https://github.com/klzgrad/naiveproxy) (Caddy + [klzgrad/forwardproxy](https://github.com/klzgrad/forwardproxy) naïve fork). ACME TLS on the domain, per-client basic auth, `naive+https://` share links. Requires free TCP **80** and **443**.
|
* **NaiveProxy** (stable): HTTPS/HTTP2 camouflage proxy via [klzgrad/naiveproxy](https://github.com/klzgrad/naiveproxy) (Caddy + [klzgrad/forwardproxy](https://github.com/klzgrad/forwardproxy) naïve fork). ACME TLS on the domain, per-client basic auth, `naive+https://` share links. Requires free TCP **80** and **443**. **Use Karing** as the client — do **not** use v2rayN; other clients are untested.
|
||||||
* **Telemt (Telegram MTProxy)**: High-performance Telegram MTProxy with TLS emulation and comprehensive management (quotas, IP limits, real-time session tracking). Robust install path that auto-configures Docker's official apt/yum repository when needed.
|
* **Telemt (Telegram MTProxy)**: High-performance Telegram MTProxy with TLS emulation and comprehensive management (quotas, IP limits, real-time session tracking). Robust install path that auto-configures Docker's official apt/yum repository when needed.
|
||||||
* **Cloudflare WARP**: Add and manage WARP-powered connectivity from the panel for routing and network flexibility.
|
* **Cloudflare WARP**: Add and manage WARP-powered connectivity from the panel for routing and network flexibility.
|
||||||
* **🛠 Services**:
|
* **🛠 Services**:
|
||||||
@@ -222,6 +222,25 @@ GitHub Actions workflows in `.github/workflows/`:
|
|||||||
|
|
||||||
## 📋 Fix / changelog (this fork)
|
## 📋 Fix / changelog (this fork)
|
||||||
|
|
||||||
|
### v2.3.0
|
||||||
|
* **Cascade removed** — double-VPN feature dropped from the panel (manager, API, UI, i18n).
|
||||||
|
|
||||||
|
### v2.2.1
|
||||||
|
* **Cascade egress verify fix**: bind curl to ENTRY `awg0`/`wg0` IP (client-subnet policy route), not cascade peer Address.
|
||||||
|
* Soft-warn (no rollback) when policy route via `cascade` is OK but public-IP echo services fail.
|
||||||
|
|
||||||
|
### v2.2.0
|
||||||
|
* **Cascade (double VPN) restored** — later removed in **v2.3.0**.
|
||||||
|
* Inspired by [ryderams/amneziawg-cascade](https://github.com/ryderams/amneziawg-cascade); logic ran over panel SSH (**no remote curl|bash**).
|
||||||
|
|
||||||
|
### v2.1.0
|
||||||
|
* **NaiveProxy — stable**: production-ready install (Caddy + klzgrad/forwardproxy), share links always include `:443`.
|
||||||
|
* **Client notes (important)**:
|
||||||
|
* **Do not use v2rayN** with NaiveProxy (broken DNS/IPv6 behaviour → latency −1 ms even when the server is fine).
|
||||||
|
* **Karing** — confirmed working.
|
||||||
|
* Other clients — untested / use at your own risk.
|
||||||
|
* Share-link and config UI hints updated accordingly.
|
||||||
|
|
||||||
### v2.0.0-beta
|
### v2.0.0-beta
|
||||||
* **NaiveProxy** (Beta): install [klzgrad/naiveproxy](https://github.com/klzgrad/naiveproxy) server via Caddy + naïve [forwardproxy](https://github.com/klzgrad/forwardproxy) — domain TLS (ACME), per-client basic auth, `naive+https://` share links.
|
* **NaiveProxy** (Beta): install [klzgrad/naiveproxy](https://github.com/klzgrad/naiveproxy) server via Caddy + naïve [forwardproxy](https://github.com/klzgrad/forwardproxy) — domain TLS (ACME), per-client basic auth, `naive+https://` share links.
|
||||||
* Marketplace / server page / users / invites / Telegram / backups include NaiveProxy.
|
* Marketplace / server page / users / invites / Telegram / backups include NaiveProxy.
|
||||||
@@ -245,7 +264,7 @@ GitHub Actions workflows in `.github/workflows/`:
|
|||||||
### v1.6.0
|
### v1.6.0
|
||||||
* **3x-ui multi-panel**: register several 3x-ui servers in Settings; pick a panel and load VLESS inbounds over its API when creating users/invites (share link comes from 3x-ui).
|
* **3x-ui multi-panel**: register several 3x-ui servers in Settings; pick a panel and load VLESS inbounds over its API when creating users/invites (share link comes from 3x-ui).
|
||||||
* **Docker / CI**: refreshed `Dockerfile` + compose, `.env.example`, CI checks, GHCR image workflow.
|
* **Docker / CI**: refreshed `Dockerfile` + compose, `.env.example`, CI checks, GHCR image workflow.
|
||||||
* **Cascade removed** for now (pending redesign — showed active while internet often did not work).
|
* **Cascade** was temporarily removed in this release (briefly restored in v2.2.x, removed again in **v2.3.0**).
|
||||||
* **WG/AWG backups**: ZIP export of client `.conf` + restore with loading feedback.
|
* **WG/AWG backups**: ZIP export of client `.conf` + restore with loading feedback.
|
||||||
* **API performance**: in-memory data cache, faster server check/stats.
|
* **API performance**: in-memory data cache, faster server check/stats.
|
||||||
* **Share / guest**: one-tap “Copy key”.
|
* **Share / guest**: one-tap “Copy key”.
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ else:
|
|||||||
application_path = os.path.dirname(__file__)
|
application_path = os.path.dirname(__file__)
|
||||||
|
|
||||||
DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export
|
DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export
|
||||||
CURRENT_VERSION = "v2.0.0-beta"
|
CURRENT_VERSION = "v2.3.0"
|
||||||
RELEASES_REPO_URL = "https://git.evilfox.cc/test2/Amnezia-Web-Panel-main"
|
RELEASES_REPO_URL = "https://git.evilfox.cc/test2/Amnezia-Web-Panel-main"
|
||||||
RELEASES_API_LATEST = "https://git.evilfox.cc/api/v1/repos/test2/Amnezia-Web-Panel-main/releases/latest"
|
RELEASES_API_LATEST = "https://git.evilfox.cc/api/v1/repos/test2/Amnezia-Web-Panel-main/releases/latest"
|
||||||
BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin'))
|
BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin'))
|
||||||
@@ -151,7 +151,7 @@ def load_translations():
|
|||||||
if f.endswith('.json'):
|
if f.endswith('.json'):
|
||||||
lang = f.split('.')[0]
|
lang = f.split('.')[0]
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(trans_dir, f), 'r', encoding='utf-8') as tf:
|
with open(os.path.join(trans_dir, f), 'r', encoding='utf-8-sig') as tf:
|
||||||
TRANSLATIONS[lang] = json.load(tf)
|
TRANSLATIONS[lang] = json.load(tf)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error loading translation {f}: {e}")
|
logger.error(f"Error loading translation {f}: {e}")
|
||||||
@@ -1742,7 +1742,6 @@ class HysteriaSettingsRequest(BaseModel):
|
|||||||
email: Optional[str] = None
|
email: Optional[str] = None
|
||||||
renew_ssl: bool = False
|
renew_ssl: bool = False
|
||||||
|
|
||||||
|
|
||||||
class ProtocolRequest(BaseModel):
|
class ProtocolRequest(BaseModel):
|
||||||
protocol: str = 'awg'
|
protocol: str = 'awg'
|
||||||
|
|
||||||
@@ -2300,7 +2299,7 @@ async def server_detail(request: Request, server_id: int):
|
|||||||
return RedirectResponse(url='/')
|
return RedirectResponse(url='/')
|
||||||
server = data['servers'][server_id]
|
server = data['servers'][server_id]
|
||||||
users_list = data.get('users', [])
|
users_list = data.get('users', [])
|
||||||
return tpl(request, 'server.html', server=server, server_id=server_id, users=users_list, all_servers=data['servers'])
|
return tpl(request, 'server.html', server=server, server_id=server_id, users=users_list)
|
||||||
|
|
||||||
|
|
||||||
@app.get('/users', response_class=HTMLResponse, tags=["System Templates"])
|
@app.get('/users', response_class=HTMLResponse, tags=["System Templates"])
|
||||||
@@ -3231,6 +3230,7 @@ async def api_hysteria_update_settings(request: Request, server_id: int, req: Hy
|
|||||||
return JSONResponse({'error': str(e)}, status_code=500)
|
return JSONResponse({'error': str(e)}, status_code=500)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.post('/api/servers/{server_id}/uninstall', tags=["Protocols"])
|
@app.post('/api/servers/{server_id}/uninstall', tags=["Protocols"])
|
||||||
async def api_uninstall_protocol(request: Request, server_id: int, req: ProtocolRequest):
|
async def api_uninstall_protocol(request: Request, server_id: int, req: ProtocolRequest):
|
||||||
if not _check_admin(request):
|
if not _check_admin(request):
|
||||||
|
|||||||
@@ -232,9 +232,13 @@ class NaiveProxyManager:
|
|||||||
# Keep a non-shared bootstrap user so the proxy is never open
|
# Keep a non-shared bootstrap user so the proxy is never open
|
||||||
auth_lines = [f' basic_auth bootstrap {_rand_token(24)}']
|
auth_lines = [f' basic_auth bootstrap {_rand_token(24)}']
|
||||||
auth_block = '\n'.join(auth_lines)
|
auth_block = '\n'.join(auth_lines)
|
||||||
|
# probe_resistance looks like a fake domain to browsers
|
||||||
probe = (probe_path or _rand_token(12)).strip('/')
|
probe = (probe_path or _rand_token(12)).strip('/')
|
||||||
|
if '.' not in probe:
|
||||||
|
probe = f'{probe}.com'
|
||||||
return f"""{{
|
return f"""{{
|
||||||
order forward_proxy before file_server
|
order forward_proxy before file_server
|
||||||
|
admin off
|
||||||
log {{
|
log {{
|
||||||
exclude http.log.error
|
exclude http.log.error
|
||||||
}}
|
}}
|
||||||
@@ -242,16 +246,14 @@ class NaiveProxyManager:
|
|||||||
:443, {domain} {{
|
:443, {domain} {{
|
||||||
tls {email}
|
tls {email}
|
||||||
encode
|
encode
|
||||||
route {{
|
forward_proxy {{
|
||||||
forward_proxy {{
|
|
||||||
{auth_block}
|
{auth_block}
|
||||||
hide_ip
|
hide_ip
|
||||||
hide_via
|
hide_via
|
||||||
probe_resistance {probe}
|
probe_resistance {probe}
|
||||||
}}
|
}}
|
||||||
file_server {{
|
file_server {{
|
||||||
root /var/www/html
|
root /var/www/html
|
||||||
}}
|
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
@@ -398,13 +400,21 @@ CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile
|
|||||||
self._start_container()
|
self._start_container()
|
||||||
|
|
||||||
def _build_share_uri(self, domain, username, password, name, port=443):
|
def _build_share_uri(self, domain, username, password, name, port=443):
|
||||||
|
"""v2rayN / NekoRay / sing-box compatible share link.
|
||||||
|
|
||||||
|
Always include an explicit port — without `:443` v2rayN often treats
|
||||||
|
the port as empty/80 and shows «Свойство Порт недопустимо» / SSL errors.
|
||||||
|
"""
|
||||||
|
# Keep credentials unescaped when alphanumeric (our generator); quote otherwise.
|
||||||
user = quote(username or '', safe='')
|
user = quote(username or '', safe='')
|
||||||
pw = quote(password or '', safe='')
|
pw = quote(password or '', safe='')
|
||||||
host = domain or ''
|
host = (domain or '').strip().lower()
|
||||||
port = int(port or 443)
|
port = int(port or 443)
|
||||||
authority = f'{user}:{pw}@{host}' if port == 443 else f'{user}:{pw}@{host}:{port}'
|
if port < 1 or port > 65535:
|
||||||
fragment = quote(name or 'naiveproxy', safe='')
|
port = 443
|
||||||
return f'naive+https://{authority}#{fragment}'
|
# Remark: ASCII-safe, no spaces that break some importers
|
||||||
|
remark = re.sub(r'[^\w.\-]+', '-', (name or 'naiveproxy').strip())[:48] or 'naiveproxy'
|
||||||
|
return f'naive+https://{user}:{pw}@{host}:{port}#{remark}'
|
||||||
|
|
||||||
# ===================== INSTALL / REMOVE =====================
|
# ===================== INSTALL / REMOVE =====================
|
||||||
|
|
||||||
|
|||||||
+20
-6
@@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% from "macros/icons.html" import icon %}
|
{% from "macros/icons.html" import icon %}
|
||||||
|
|
||||||
{% block title_extra %} — {{ server.name }}{% endblock %}
|
{% block title_extra %} — {{ server.name }}{% endblock %}
|
||||||
@@ -325,10 +325,11 @@
|
|||||||
<div class="protocol-icon">{{ icon('link') }}</div>
|
<div class="protocol-icon">{{ icon('link') }}</div>
|
||||||
<div class="flex gap-sm" id="naiveproxy-ctrl" style="display:none!important;"></div>
|
<div class="flex gap-sm" id="naiveproxy-ctrl" style="display:none!important;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="protocol-name">NaiveProxy</div>
|
<div class="protocol-name">NaiveProxy <span
|
||||||
<div class="protocol-desc">
|
style="font-size:0.65rem; background:var(--success, #16a34a); color:#fff; padding:2px 6px; border-radius:8px; vertical-align:middle;">STABLE</span></div>
|
||||||
{{ _('naiveproxy_desc') }}
|
<div class="protocol-desc">
|
||||||
</div>
|
{{ _('naiveproxy_desc') }}
|
||||||
|
</div>
|
||||||
<div class="protocol-status" id="naiveproxy-status">
|
<div class="protocol-status" id="naiveproxy-status">
|
||||||
<span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
|
<span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -743,6 +744,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-hint">{{ _('naiveproxy_install_hint') }}</div>
|
<div class="form-hint">{{ _('naiveproxy_install_hint') }}</div>
|
||||||
<div class="form-hint" style="margin-top: var(--space-sm);">{{ _('naiveproxy_port_hint') }}</div>
|
<div class="form-hint" style="margin-top: var(--space-sm);">{{ _('naiveproxy_port_hint') }}</div>
|
||||||
|
<div class="form-hint" style="margin-top: var(--space-sm); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35);">{{ _('naiveproxy_client_hint') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@@ -986,6 +988,7 @@
|
|||||||
<div class="config-panel active" id="panel-conf">
|
<div class="config-panel active" id="panel-conf">
|
||||||
<div class="config-display">
|
<div class="config-display">
|
||||||
<div class="config-text" id="configText"></div>
|
<div class="config-text" id="configText"></div>
|
||||||
|
<div class="form-hint hidden" id="configClientHint" style="margin-top: var(--space-sm);"></div>
|
||||||
<div class="config-actions">
|
<div class="config-actions">
|
||||||
<button class="btn btn-secondary btn-sm" onclick="copyToClipboard(currentConfig)" style="flex:1">
|
<button class="btn btn-secondary btn-sm" onclick="copyToClipboard(currentConfig)" style="flex:1">
|
||||||
{{ _('copy') }}
|
{{ _('copy') }}
|
||||||
@@ -1097,7 +1100,7 @@
|
|||||||
{ proto: 'xray', category: 'protocols', icon: 'zap', title: 'Xray (VLESS-Reality)', descKey: 'xray_desc' },
|
{ proto: 'xray', category: 'protocols', icon: 'zap', title: 'Xray (VLESS-Reality)', descKey: 'xray_desc' },
|
||||||
{ proto: 'telemt', category: 'protocols', icon: 'plane', title: 'Telemt (Telegram Proxy)', descKey: 'telemt_desc' },
|
{ proto: 'telemt', category: 'protocols', icon: 'plane', title: 'Telemt (Telegram Proxy)', descKey: 'telemt_desc' },
|
||||||
{ proto: 'hysteria', category: 'protocols', icon: 'refresh', title: 'Hysteria 2', descKey: 'hysteria_desc' },
|
{ proto: 'hysteria', category: 'protocols', icon: 'refresh', title: 'Hysteria 2', descKey: 'hysteria_desc' },
|
||||||
{ proto: 'naiveproxy', category: 'protocols', icon: 'link', title: 'NaiveProxy', descKey: 'naiveproxy_desc' },
|
{ proto: 'naiveproxy', category: 'protocols', icon: 'link', title: 'NaiveProxy', descKey: 'naiveproxy_desc', badge: 'STABLE' },
|
||||||
{ proto: 'wireguard', category: 'protocols', icon: 'lock', title: 'WireGuard', descKey: 'wireguard_desc' },
|
{ proto: 'wireguard', category: 'protocols', icon: 'lock', title: 'WireGuard', descKey: 'wireguard_desc' },
|
||||||
{ proto: 'dns', category: 'services', icon: 'search', title: 'AmneziaDNS', descKey: 'dns_desc' },
|
{ proto: 'dns', category: 'services', icon: 'search', title: 'AmneziaDNS', descKey: 'dns_desc' },
|
||||||
{ proto: 'adguard', category: 'services', icon: 'shield-check', title: 'AdGuard Home', descKey: 'adguard_desc' },
|
{ proto: 'adguard', category: 'services', icon: 'shield-check', title: 'AdGuard Home', descKey: 'adguard_desc' },
|
||||||
@@ -2840,6 +2843,16 @@
|
|||||||
document.getElementById('configModalTitle').textContent = `${_('config')} — ${connName}`;
|
document.getElementById('configModalTitle').textContent = `${_('config')} — ${connName}`;
|
||||||
document.getElementById('configText').textContent = result.config;
|
document.getElementById('configText').textContent = result.config;
|
||||||
document.getElementById('vpnLinkText').textContent = currentVpnLink;
|
document.getElementById('vpnLinkText').textContent = currentVpnLink;
|
||||||
|
const hintEl = document.getElementById('configClientHint');
|
||||||
|
if (hintEl) {
|
||||||
|
if (protoBase(proto) === 'naiveproxy') {
|
||||||
|
hintEl.textContent = _('naiveproxy_client_hint');
|
||||||
|
hintEl.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
hintEl.textContent = '';
|
||||||
|
hintEl.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
switchConfigTab('conf');
|
switchConfigTab('conf');
|
||||||
openModal('configModal');
|
openModal('configModal');
|
||||||
generateQR(result.config);
|
generateQR(result.config);
|
||||||
@@ -2952,6 +2965,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ========== Init ==========
|
// ========== Init ==========
|
||||||
applyInstalledAppsVisibility();
|
applyInstalledAppsVisibility();
|
||||||
checkServer();
|
checkServer();
|
||||||
|
|||||||
+577
-576
File diff suppressed because it is too large
Load Diff
+520
-519
File diff suppressed because it is too large
Load Diff
+520
-519
File diff suppressed because it is too large
Load Diff
+577
-576
File diff suppressed because it is too large
Load Diff
+520
-519
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user