import logging import os from flask import Blueprint, request, jsonify, current_app logger = logging.getLogger('picell') bp = Blueprint('containers', __name__) @bp.route('/api/containers', methods=['GET']) def list_containers(): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify(container_manager.list_containers()) except Exception as e: logger.error(f"Error listing containers: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/containers//start', methods=['POST']) def start_container(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify({'started': container_manager.start_container(name)}) except Exception as e: logger.error(f"Error starting container {name}: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/containers//stop', methods=['POST']) def stop_container(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify({'stopped': container_manager.stop_container(name)}) except Exception as e: logger.error(f"Error stopping container {name}: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/containers//restart', methods=['POST']) def restart_container(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify({'restarted': container_manager.restart_container(name)}) except Exception as e: logger.error(f"Error restarting container {name}: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/containers//logs', methods=['GET']) def get_container_logs(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 tail = request.args.get('tail', default=100, type=int) return jsonify({'logs': container_manager.get_container_logs(name, tail=tail)}) except Exception as e: logger.error(f"Error getting logs for container {name}: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/containers//stats', methods=['GET']) def get_container_stats(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify(container_manager.get_container_stats(name)) except Exception as e: logger.error(f"Error getting stats for container {name}: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/containers', methods=['POST']) def create_container(): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 data = request.get_json(silent=True) if not data or 'image' not in data: return jsonify({'error': 'Missing image parameter'}), 400 name = data.get('name', '') env = data.get('env', {}) secrets = data.get('secrets', []) if secrets: for secret_name in secrets: secret_value = current_app.vault_manager.get_secret(secret_name) if secret_value is not None: env[secret_name] = secret_value volumes = data.get('volumes', {}) if volumes: allowed_prefixes = ('/home/roof/pic/data/', '/home/roof/pic/config/', '/tmp/') for host_path in volumes.keys(): resolved = os.path.realpath(str(host_path)) if not any(resolved.startswith(p) for p in allowed_prefixes): return jsonify({'error': f'Volume mount not allowed: {host_path}'}), 403 result = container_manager.create_container( image=data['image'], name=name, env=env, volumes=volumes, command=data.get('command', ''), ports=data.get('ports', {}) ) if 'error' in result: return jsonify(result), 500 return jsonify(result) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/containers/', methods=['DELETE']) def remove_container(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 force = request.args.get('force', default=False, type=bool) return jsonify({'removed': container_manager.remove_container(name, force=force)}) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/images', methods=['GET']) def list_images(): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify(container_manager.list_images()) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/images/pull', methods=['POST']) def pull_image(): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 data = request.get_json(silent=True) if not data or 'image' not in data: return jsonify({'error': 'Missing image parameter'}), 400 result = container_manager.pull_image(data['image']) if 'error' in result: return jsonify(result), 500 return jsonify(result) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/images/', methods=['DELETE']) def remove_image(image): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 force = request.args.get('force', default=False, type=bool) return jsonify({'removed': container_manager.remove_image(image, force=force)}) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/volumes', methods=['GET']) def list_volumes(): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 return jsonify(container_manager.list_volumes()) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/volumes', methods=['POST']) def create_volume(): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 data = request.get_json(silent=True) if not data or 'name' not in data: return jsonify({'error': 'Missing name parameter'}), 400 result = container_manager.create_volume(data['name']) if 'error' in result: return jsonify(result), 500 return jsonify(result) except Exception as e: return jsonify({'error': str(e)}), 500 @bp.route('/api/volumes/', methods=['DELETE']) def remove_volume(name): try: from app import container_manager, is_local_request if not is_local_request(): return jsonify({'error': 'Access denied'}), 403 force = request.args.get('force', default=False, type=bool) return jsonify({'removed': container_manager.remove_volume(name, force=force)}) except Exception as e: return jsonify({'error': str(e)}), 500