feat: WMT client versioning, release management and force-update playbook

- api/wmt.py: add GET /api/wmt/client/version and GET /api/wmt/client/download endpoints; rewrite submit_update_request with dedup logic
- web/wmt.py: add releases, releases_upload, releases_delete, releases_build routes; build-from-folder excludes hidden/data/venv/pyc files
- web/main.py: admin per-device delete route; clear-device-logs route; pass devices list to admin template
- templates/wmt/releases.html: new release management page (current release info, upload form, build-from-folder card)
- templates/admin.html: replace nuclear clear-devices with clear-logs + per-device delete table
- templates/base.html: add Client Releases nav link in WMT sidebar section
- templates/ansible/execute.html: add Update WMT Code playbook card
- ansible/playbooks/update_wmt_code.yml: rsync WMT_project to clients excluding data/; backs up app.py; restarts wmt service
- ansible_service.py: register update_wmt_code description
- .gitignore: whitelist update_wmt_code.yml
This commit is contained in:
ske087
2026-05-13 16:36:17 +03:00
parent ccad5c1201
commit f1449285ba
10 changed files with 978 additions and 40 deletions
+47 -2
View File
@@ -516,7 +516,18 @@ def admin():
logging.error(f'Admin inventory stats error: {e}')
stats['inventory_hosts'] = '?'
stats['inventory_groups_yaml'] = '?'
return render_template('admin.html', stats=stats)
# Pass registered devices for per-device delete UI
devices = []
try:
with get_db().get_session() as session:
devices = [
{'id': d.id, 'hostname': d.hostname, 'nume_masa': d.nume_masa,
'mac_address': d.mac_address, 'device_ip': d.device_ip}
for d in session.query(Device).order_by(Device.nume_masa).all()
]
except Exception as e:
logging.error(f'Admin device list error: {e}')
return render_template('admin.html', stats=stats, devices=devices)
@main_bp.route('/admin/clear/logs', methods=['POST'])
@@ -532,9 +543,43 @@ def admin_clear_logs():
return jsonify({'success': False, 'error': str(e)}), 500
@main_bp.route('/admin/clear/device-logs', methods=['POST'])
def admin_clear_device_logs():
"""Delete all log entries from the database (devices stay intact)."""
try:
with get_db().get_session() as session:
count = session.query(LogEntry).delete()
session.commit()
return jsonify({'success': True, 'deleted': count})
except Exception as e:
logging.error(f'Admin clear device logs error: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@main_bp.route('/admin/delete/device/<int:device_id>', methods=['POST'])
def admin_delete_device(device_id):
"""Delete a single registered device and its log entries."""
try:
with get_db().get_session() as session:
device = session.query(Device).filter_by(id=device_id).first()
if not device:
return jsonify({'success': False, 'error': 'Device not found'}), 404
name = device.nume_masa or device.hostname
session.execute(text('DELETE FROM device_inventory_groups WHERE device_id = :id'), {'id': device_id})
session.query(LogEntry).filter_by(device_id=device_id).delete()
session.query(WMTUpdateRequest).filter_by(device_id=device_id).delete()
session.delete(device)
session.commit()
logging.info(f'Admin deleted device {device_id} ({name})')
return jsonify({'success': True, 'name': name})
except Exception as e:
logging.error(f'Admin delete device {device_id} error: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@main_bp.route('/admin/clear/devices', methods=['POST'])
def admin_clear_devices():
"""Delete all devices (and their log entries) from the database."""
"""Delete ALL devices (and their log entries) from the database."""
try:
with get_db().get_session() as session:
session.execute(text('DELETE FROM device_inventory_groups'))