feat(admin): system config and global executions history API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-05-16 09:18:39 +00:00
parent d8513d055d
commit fe357668b4
2 changed files with 142 additions and 0 deletions

View File

@ -0,0 +1,75 @@
"""Admin API: configuració sistema i historial d'execucions."""
from flask import request, jsonify
from sqlalchemy import select, desc, func
from . import admin_bp
from .. import require_admin
from ..db import get_session
from ..models.db_models import SystemConfigModel, SimulationModel, ProjectModel, UserModel
@admin_bp.route('/config', methods=['GET'])
@require_admin
def get_config():
with get_session() as db:
entries = db.execute(select(SystemConfigModel)).scalars().all()
result = []
for e in entries:
result.append({
'key': e.key,
'value': '●●●●' if e.is_secret else e.value,
'value_type': e.value_type,
'group': e.group,
'label': e.label,
'description': e.description,
'is_secret': e.is_secret,
})
return jsonify({'success': True, 'data': result})
@admin_bp.route('/config', methods=['PATCH'])
@require_admin
def patch_config():
data = request.get_json(silent=True) or {}
with get_session() as db:
for key, value in data.items():
entry = db.get(SystemConfigModel, key)
if entry:
entry.value = str(value)
db.commit()
return jsonify({'success': True})
@admin_bp.route('/executions', methods=['GET'])
@require_admin
def list_executions():
page = request.args.get('page', 1, type=int)
page_size = request.args.get('pageSize', 20, type=int)
filter_user_id = request.args.get('user_id')
offset = (page - 1) * page_size
with get_session() as db:
stmt = (
select(SimulationModel, ProjectModel, UserModel)
.join(ProjectModel, SimulationModel.project_id == ProjectModel.id)
.outerjoin(UserModel, ProjectModel.user_id == UserModel.id)
.order_by(desc(SimulationModel.created_at))
)
if filter_user_id:
stmt = stmt.where(ProjectModel.user_id == filter_user_id)
total = db.execute(select(func.count()).select_from(stmt.subquery())).scalar()
rows = db.execute(stmt.offset(offset).limit(page_size)).all()
result = []
for sim, proj, user in rows:
result.append({
'simulation_id': sim.id,
'project_id': proj.id,
'project_name': proj.name,
'user_email': user.email if user else None,
'status': sim.status,
'platform': sim.platform,
'rounds_total': sim.rounds_total,
'rounds_completed': sim.rounds_completed,
'created_at': sim.created_at.isoformat(),
})
return jsonify({'success': True, 'data': result, 'total': total, 'page': page, 'pageSize': page_size})

View File

@ -0,0 +1,67 @@
"""Tests per a l'API d'administració."""
import pytest
from unittest.mock import MagicMock, patch
@pytest.fixture
def app(in_memory_db):
import backend.app.db as db_module
saved_engine = db_module._engine
saved_session = db_module._SessionLocal
def _noop(url):
db_module._engine = saved_engine
db_module._SessionLocal = saved_session
with patch('backend.app.db.init_db', side_effect=_noop):
from backend.app import create_app
application = create_app()
application.config['TESTING'] = True
application.extensions['storage'] = MagicMock()
db_module._engine = saved_engine
db_module._SessionLocal = saved_session
return application
@pytest.fixture
def client(app):
with app.test_client() as c:
yield c
def test_get_config_empty(client, in_memory_db):
res = client.get('/api/admin/config')
assert res.status_code == 200
data = res.get_json()
assert data['success'] is True
assert isinstance(data['data'], list)
def test_patch_config(client, in_memory_db):
from backend.app.models.db_models import SystemConfigModel
from backend.app.db import get_session
with get_session() as db:
db.add(SystemConfigModel(
key='llm.model_name', value='qwen-plus',
value_type='string', group='llm',
label='Model LLM', description='Nom del model LLM principal',
is_secret=False
))
db.commit()
res = client.patch('/api/admin/config', json={'llm.model_name': 'gpt-4o'})
assert res.status_code == 200
res2 = client.get('/api/admin/config')
entries = res2.get_json()['data']
entry = next(e for e in entries if e['key'] == 'llm.model_name')
assert entry['value'] == 'gpt-4o'
def test_get_executions_empty(client, in_memory_db):
res = client.get('/api/admin/executions')
assert res.status_code == 200
data = res.get_json()
assert data['success'] is True
assert data['data'] == []