|
from flask import Flask, request, jsonify
|
|
import subprocess
|
|
import os
|
|
import tempfile
|
|
import uuid
|
|
import time
|
|
import logging
|
|
from flask_cors import CORS
|
|
import gradio as gr
|
|
import requests
|
|
|
|
app = Flask(__name__)
|
|
CORS(app)
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
TEMP_DIR = os.path.join(tempfile.gettempdir(), '42coderunner')
|
|
os.makedirs(TEMP_DIR, exist_ok=True)
|
|
|
|
|
|
MAX_EXECUTION_TIME = 10
|
|
|
|
|
|
|
|
API_BASE_URL = os.environ.get("SPACE_HOST")
|
|
if API_BASE_URL:
|
|
|
|
|
|
API_BASE_URL = f"https://{API_BASE_URL}"
|
|
else:
|
|
|
|
API_BASE_URL = "http://localhost:5000"
|
|
|
|
API_EXECUTE_URL = f"{API_BASE_URL}/api/execute"
|
|
|
|
|
|
|
|
@app.route('/api/execute', methods=['POST'])
|
|
def execute_code():
|
|
try:
|
|
|
|
data = request.get_json()
|
|
if not data or 'code' not in data:
|
|
return jsonify({'success': False, 'error': 'No se proporcion贸 c贸digo'}), 400
|
|
|
|
code = data['code']
|
|
|
|
|
|
job_id = str(uuid.uuid4())
|
|
|
|
|
|
code_file = os.path.join(TEMP_DIR, f"{job_id}.c")
|
|
executable = os.path.join(TEMP_DIR, f"{job_id}.exe")
|
|
|
|
|
|
with open(code_file, 'w') as f:
|
|
f.write(code)
|
|
|
|
|
|
logger.info(f"Compilando c贸digo para job {job_id}")
|
|
compile_process = subprocess.run(
|
|
['gcc', code_file, '-o', executable],
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
|
|
if compile_process.returncode != 0:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': compile_process.stderr
|
|
})
|
|
|
|
|
|
logger.info(f"Ejecutando c贸digo para job {job_id}")
|
|
try:
|
|
start_time = time.time()
|
|
run_process = subprocess.run(
|
|
[executable],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=MAX_EXECUTION_TIME
|
|
)
|
|
execution_time = time.time() - start_time
|
|
|
|
|
|
result = {
|
|
'success': run_process.returncode == 0,
|
|
'output': run_process.stdout,
|
|
'error': run_process.stderr,
|
|
'execution_time': execution_time
|
|
}
|
|
|
|
except subprocess.TimeoutExpired:
|
|
result = {
|
|
'success': False,
|
|
'error': f'La ejecuci贸n excedi贸 el tiempo l铆mite de {MAX_EXECUTION_TIME} segundos'
|
|
}
|
|
|
|
|
|
try:
|
|
os.remove(code_file)
|
|
if os.path.exists(executable):
|
|
os.remove(executable)
|
|
except Exception as e:
|
|
logger.error(f"Error al limpiar archivos temporales: {e}")
|
|
|
|
return jsonify(result)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error inesperado: {e}")
|
|
return jsonify({'success': False, 'error': f'Error interno del servidor: {str(e)}'}), 500
|
|
|
|
@app.route('/api/health', methods=['GET'])
|
|
def health_check():
|
|
"""Basic health check endpoint."""
|
|
logger.info("Health check requested.")
|
|
|
|
|
|
return jsonify({'status': 'ok', 'timestamp': time.time()})
|
|
|
|
|
|
|
|
def run_c_code(c_code):
|
|
"""Funci贸n que se ejecuta cuando el usuario env铆a c贸digo C a trav茅s de Gradio."""
|
|
logger.info(f"Solicitud Gradio recibida. URL API: {API_EXECUTE_URL}")
|
|
try:
|
|
response = requests.post(API_EXECUTE_URL, json={'code': c_code}, timeout=MAX_EXECUTION_TIME + 5)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
|
|
if data.get('success'):
|
|
output = f"--- Salida ({data.get('execution_time', 0):.4f}s) ---\n{data.get('output', '')}"
|
|
if data.get('error'):
|
|
output += f"\n\n--- Errores (stderr) ---\n{data['error']}"
|
|
return output
|
|
else:
|
|
return f"--- Error ---\n{data.get('error', 'Error desconocido')}"
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
logger.error(f"Error de red al llamar a la API: {e}")
|
|
return f"Error de red al conectar con el backend: {e}"
|
|
except Exception as e:
|
|
logger.error(f"Error inesperado en la interfaz Gradio: {e}")
|
|
return f"Error inesperado en la interfaz: {str(e)}"
|
|
|
|
|
|
EXAMPLE_CODE = """#include <stdio.h>
|
|
|
|
int main() {
|
|
printf("隆Hola desde 42CodeRunner!\n");
|
|
return 0;
|
|
}
|
|
"""
|
|
|
|
|
|
iface = gr.Interface(
|
|
fn=run_c_code,
|
|
inputs=gr.Code(language="c", label="C贸digo C", value=EXAMPLE_CODE),
|
|
outputs=gr.Textbox(label="Resultado de la Ejecuci贸n", lines=15),
|
|
title="馃強u200d鈾傦笍 42CodeRunner",
|
|
description="Escribe tu c贸digo C, haz clic en 'Submit' y mira la magia suceder. Ejecutado de forma segura en el backend.",
|
|
allow_flagging='never'
|
|
)
|
|
|
|
|
|
|
|
|
|
with gr.Blocks(title="馃強u200d鈾傦笍 42CodeRunner") as demo:
|
|
|
|
iface.render()
|
|
|
|
|
|
|
|
demo = gr.mount_wsgi_app(demo, app, path="/api")
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
port = int(os.environ.get('PORT', 7860))
|
|
|
|
|
|
demo.launch(server_name='0.0.0.0', server_port=port) |