Add one-click panel auto-update from git.evilfox.cc (v2.4.0).

Check Gitea releases in Settings and install the latest tag via git fetch/checkout with pip sync and restart when running from a git checkout.
This commit is contained in:
orohi
2026-07-28 09:28:15 +03:00
parent 6206697e3b
commit fd257a59f7
9 changed files with 451 additions and 17 deletions
+53 -4
View File
@@ -46,6 +46,7 @@ from managers.awg_manager import AWGManager
from managers.xray_manager import XrayManager
from managers.wireguard_manager import WireGuardManager
from managers.backup_manager import BackupManager
from managers.update_manager import UpdateManager, version_gt, api_latest_url, repo_url
import telegram_bot as tg_bot
# Configure logging
@@ -101,9 +102,9 @@ else:
application_path = os.path.dirname(__file__)
DATA_FILE = os.path.join(application_path, 'data.json') # legacy JSON; used only for one-shot import / export
CURRENT_VERSION = "v2.3.0"
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"
CURRENT_VERSION = "v2.4.0"
RELEASES_REPO_URL = repo_url()
RELEASES_API_LATEST = api_latest_url()
BIN_DIR = os.environ.get('TUNNEL_BIN_DIR', os.path.join(application_path, 'bin'))
TUNNEL_STATE_FILE = os.environ.get('TUNNEL_STATE_FILE', os.path.join(application_path, 'tunnels_state.json'))
@@ -1992,6 +1993,11 @@ class TunnelStartRequest(BaseModel):
authtoken: Optional[str] = None
class ApplyUpdateRequest(BaseModel):
target_version: Optional[str] = None
allow_dirty: bool = False
# ======================== Startup ========================
@app.on_event("startup")
@@ -5498,6 +5504,8 @@ async def api_check_updates(request: Request):
if not _check_admin(request):
return JSONResponse({'error': 'Forbidden'}, status_code=403)
try:
updater = UpdateManager(application_path, CURRENT_VERSION)
mode = updater.detect_mode()
async with httpx.AsyncClient(timeout=15.0, follow_redirects=True) as client:
resp = await client.get(
RELEASES_API_LATEST,
@@ -5511,20 +5519,61 @@ async def api_check_updates(request: Request):
data = resp.json() if resp.content else {}
latest = (data.get('tag_name') or data.get('name') or '').strip()
html_url = (data.get('html_url') or f'{RELEASES_REPO_URL}/releases').strip()
body = (data.get('body') or '')[:4000]
current = CURRENT_VERSION
update_available = bool(latest) and latest != current
update_available = bool(latest) and version_gt(latest, current)
return {
'current_version': current,
'latest_version': latest,
'update_available': update_available,
'html_url': html_url,
'releases_url': f'{RELEASES_REPO_URL}/releases',
'body': body,
'can_auto_update': bool(mode.get('can_auto_update')),
'update_mode': mode.get('mode'),
'mode': mode,
}
except Exception as e:
logger.exception('Error checking for updates')
return JSONResponse({'error': str(e)}, status_code=502)
@app.post('/api/settings/apply_update', tags=["Settings"])
async def api_apply_update(request: Request, req: ApplyUpdateRequest):
"""Pull the selected release tag from git.evilfox.cc and restart the panel."""
if not _check_admin(request):
return JSONResponse({'error': 'Forbidden'}, status_code=403)
updater = UpdateManager(application_path, CURRENT_VERSION)
target = (req.target_version or '').strip()
if not target:
# Resolve latest if UI did not pass a tag
try:
async with httpx.AsyncClient(timeout=15.0, follow_redirects=True) as client:
resp = await client.get(
RELEASES_API_LATEST,
headers={'Accept': 'application/json'},
)
if resp.status_code < 400 and resp.content:
payload = resp.json()
target = (payload.get('tag_name') or payload.get('name') or '').strip()
except Exception:
logger.exception('Failed to resolve latest tag for apply_update')
if not target:
return JSONResponse({'error': 'No target version specified'}, status_code=400)
result = await asyncio.to_thread(
updater.apply_update,
target,
bool(req.allow_dirty),
)
if result.get('status') != 'success':
return JSONResponse({'error': result.get('message') or 'Update failed', **result}, status_code=500)
if result.get('restart'):
UpdateManager.schedule_restart(1.5)
return result
@app.get('/api/settings/tunnels/status', tags=["Settings"])
async def api_tunnels_status(request: Request):
if not _check_admin(request):