125 lines
4.0 KiB
Python
125 lines
4.0 KiB
Python
"""Client download management - upload builds, serve downloads."""
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from flask import request, jsonify, send_from_directory, current_app
|
|
|
|
from app.api import api_bp
|
|
|
|
# Supported platforms and their file extensions
|
|
PLATFORMS = {
|
|
'linux': {'name': 'Linux', 'icon': 'pi-desktop', 'extensions': ['.AppImage', '.deb', '']},
|
|
'windows': {'name': 'Windows', 'icon': 'pi-microsoft', 'extensions': ['.msi', '.exe']},
|
|
'mac': {'name': 'macOS', 'icon': 'pi-apple', 'extensions': ['.dmg']},
|
|
'android': {'name': 'Android', 'icon': 'pi-android', 'extensions': ['.apk']},
|
|
'ios': {'name': 'iOS', 'icon': 'pi-apple', 'extensions': ['.ipa']},
|
|
}
|
|
|
|
|
|
def _clients_dir():
|
|
"""Get the client downloads directory."""
|
|
base = Path(current_app.config.get('UPLOAD_PATH', '/app/data/files')).parent
|
|
d = base / 'client-downloads'
|
|
d.mkdir(parents=True, exist_ok=True)
|
|
return d
|
|
|
|
|
|
def _verify_build_token():
|
|
"""Verify the build upload token from header or query param."""
|
|
expected = os.environ.get('SECRET_KEY', '')
|
|
if not expected:
|
|
return False
|
|
token = request.headers.get('X-Build-Token', '') or request.args.get('build_token', '')
|
|
return token == expected
|
|
|
|
|
|
# --- Public: list available clients ---
|
|
|
|
@api_bp.route('/clients', methods=['GET'])
|
|
def list_clients():
|
|
"""List available client downloads (public, no auth needed)."""
|
|
clients_dir = _clients_dir()
|
|
available = []
|
|
|
|
for platform, info in PLATFORMS.items():
|
|
platform_dir = clients_dir / platform
|
|
if not platform_dir.exists():
|
|
continue
|
|
|
|
files = sorted(platform_dir.iterdir(), key=lambda f: f.stat().st_mtime, reverse=True)
|
|
if not files:
|
|
continue
|
|
|
|
# Take the newest file
|
|
latest = files[0]
|
|
available.append({
|
|
'platform': platform,
|
|
'name': info['name'],
|
|
'icon': info['icon'],
|
|
'filename': latest.name,
|
|
'size': latest.stat().st_size,
|
|
'updated_at': latest.stat().st_mtime,
|
|
'download_url': f'/api/clients/{platform}/download',
|
|
})
|
|
|
|
return jsonify({
|
|
'clients': available,
|
|
'has_clients': len(available) > 0,
|
|
}), 200
|
|
|
|
|
|
@api_bp.route('/clients/<platform>/download', methods=['GET'])
|
|
def download_client(platform):
|
|
"""Download the latest client for a platform (public, no auth)."""
|
|
if platform not in PLATFORMS:
|
|
return jsonify({'error': 'Unbekannte Plattform'}), 404
|
|
|
|
clients_dir = _clients_dir()
|
|
platform_dir = clients_dir / platform
|
|
|
|
if not platform_dir.exists():
|
|
return jsonify({'error': 'Kein Client fuer diese Plattform verfuegbar'}), 404
|
|
|
|
files = sorted(platform_dir.iterdir(), key=lambda f: f.stat().st_mtime, reverse=True)
|
|
if not files:
|
|
return jsonify({'error': 'Kein Client verfuegbar'}), 404
|
|
|
|
latest = files[0]
|
|
return send_from_directory(str(platform_dir), latest.name, as_attachment=True)
|
|
|
|
|
|
# --- Build upload (authenticated with BUILD_UPLOAD_TOKEN) ---
|
|
|
|
@api_bp.route('/clients/<platform>/upload', methods=['POST'])
|
|
def upload_client(platform):
|
|
"""Upload a new client build. Authenticated with BUILD_UPLOAD_TOKEN."""
|
|
if not _verify_build_token():
|
|
return jsonify({'error': 'Ungueltiger Build-Token'}), 403
|
|
|
|
if platform not in PLATFORMS:
|
|
return jsonify({'error': 'Unbekannte Plattform'}), 404
|
|
|
|
if 'file' not in request.files:
|
|
return jsonify({'error': 'Keine Datei gesendet'}), 400
|
|
|
|
upload = request.files['file']
|
|
if not upload.filename:
|
|
return jsonify({'error': 'Leerer Dateiname'}), 400
|
|
|
|
clients_dir = _clients_dir()
|
|
platform_dir = clients_dir / platform
|
|
platform_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Remove old files for this platform (keep only latest)
|
|
for old_file in platform_dir.iterdir():
|
|
old_file.unlink()
|
|
|
|
dest = platform_dir / upload.filename
|
|
upload.save(str(dest))
|
|
|
|
return jsonify({
|
|
'message': f'{PLATFORMS[platform]["name"]} Client hochgeladen',
|
|
'filename': upload.filename,
|
|
'size': dest.stat().st_size,
|
|
}), 200
|