forked from test2/Amnezia-Web-Panel-main
Enable true one-click panel update from Settings (v2.5.1).
Download release ZIP from git.evilfox.cc when git is unavailable, add upgrade_panel API, and show a primary Update panel button.
This commit is contained in:
@@ -102,7 +102,7 @@ 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.5.0"
|
||||
CURRENT_VERSION = "v2.5.1"
|
||||
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'))
|
||||
@@ -5682,7 +5682,7 @@ async def api_check_updates(request: Request):
|
||||
'releases_url': f'{RELEASES_REPO_URL}/releases',
|
||||
'body': body,
|
||||
'can_auto_update': bool(mode.get('can_auto_update')),
|
||||
'update_mode': mode.get('mode'),
|
||||
'update_mode': mode.get('update_method') or mode.get('mode'),
|
||||
'mode': mode,
|
||||
}
|
||||
except Exception as e:
|
||||
@@ -5690,26 +5690,28 @@ async def api_check_updates(request: Request):
|
||||
return JSONResponse({'error': str(e)}, status_code=502)
|
||||
|
||||
|
||||
async def _fetch_latest_release_tag() -> str:
|
||||
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:
|
||||
raise RuntimeError(f'Release API returned HTTP {resp.status_code}')
|
||||
data = resp.json() if resp.content else {}
|
||||
return (data.get('tag_name') or data.get('name') or '').strip()
|
||||
|
||||
|
||||
@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."""
|
||||
"""Install a release tag (git checkout or archive download) 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:
|
||||
target = await _fetch_latest_release_tag()
|
||||
except Exception as e:
|
||||
logger.exception('Failed to resolve latest tag for apply_update')
|
||||
return JSONResponse({'error': str(e)}, status_code=502)
|
||||
if not target:
|
||||
return JSONResponse({'error': 'No target version specified'}, status_code=400)
|
||||
|
||||
@@ -5719,13 +5721,54 @@ async def api_apply_update(request: Request, req: ApplyUpdateRequest):
|
||||
bool(req.allow_dirty),
|
||||
)
|
||||
if result.get('status') != 'success':
|
||||
return JSONResponse({'error': result.get('message') or 'Update failed', **result}, status_code=500)
|
||||
status_code = 409 if result.get('needs_confirm_dirty') else 500
|
||||
return JSONResponse({'error': result.get('message') or 'Update failed', **result}, status_code=status_code)
|
||||
|
||||
if result.get('restart'):
|
||||
UpdateManager.schedule_restart(1.5)
|
||||
return result
|
||||
|
||||
|
||||
@app.post('/api/settings/upgrade_panel', tags=["Settings"])
|
||||
async def api_upgrade_panel(request: Request):
|
||||
"""One-click: fetch latest release, install if newer, restart."""
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
updater = UpdateManager(application_path, CURRENT_VERSION)
|
||||
mode = updater.detect_mode()
|
||||
if not mode.get('can_auto_update'):
|
||||
return JSONResponse({
|
||||
'error': (
|
||||
'In-panel update is not available for this install. '
|
||||
'Use git clone / writable app directory, or rebuild Docker on the host.'
|
||||
),
|
||||
'mode': mode,
|
||||
}, status_code=400)
|
||||
try:
|
||||
latest = await _fetch_latest_release_tag()
|
||||
except Exception as e:
|
||||
return JSONResponse({'error': str(e)}, status_code=502)
|
||||
if not latest:
|
||||
return JSONResponse({'error': 'Could not resolve latest release tag'}, status_code=502)
|
||||
if not version_gt(latest, CURRENT_VERSION):
|
||||
return {
|
||||
'status': 'success',
|
||||
'up_to_date': True,
|
||||
'current_version': CURRENT_VERSION,
|
||||
'latest_version': latest,
|
||||
'message': 'Already on the latest version',
|
||||
}
|
||||
|
||||
result = await asyncio.to_thread(updater.apply_update, latest, True)
|
||||
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)
|
||||
result['up_to_date'] = False
|
||||
result['previous_version'] = CURRENT_VERSION
|
||||
return result
|
||||
|
||||
|
||||
@app.get('/api/settings/tunnels/status', tags=["Settings"])
|
||||
async def api_tunnels_status(request: Request):
|
||||
if not _check_admin(request):
|
||||
|
||||
Reference in New Issue
Block a user