added gunicorn and updated to the last version of server monitorizare

This commit is contained in:
ske087
2026-06-07 21:28:09 +03:00
parent 0aefadbfd8
commit b97372f74d
35 changed files with 2098 additions and 255 deletions
+152 -24
View File
@@ -2,8 +2,10 @@
WMT (Workstation Management Terminal) configuration API
Handles config distribution and device update requests from WMT clients.
"""
from flask import Blueprint, request, jsonify
from flask import Blueprint, request, jsonify, send_file
from datetime import datetime
from pathlib import Path
import json
from app.models import WMTGlobalConfig, Device, WMTUpdateRequest
from config.database_config import get_db
import logging
@@ -96,8 +98,8 @@ def get_device_config(mac_address):
_, device_ts, latest_ts = _latest_config_ts(session, mac)
payload = {
# Global settings
'chrome_url': global_cfg.chrome_url,
# Global settings (device custom_chrome_url overrides global chrome_url if set)
'chrome_url': (device.custom_chrome_url if device and device.custom_chrome_url else global_cfg.chrome_url),
'chrome_local_url': global_cfg.chrome_local_url or '',
'chrome_insecure_origin': global_cfg.chrome_insecure_origin,
'card_api_base_url': global_cfg.card_api_base_url,
@@ -125,7 +127,25 @@ def get_device_config(mac_address):
@wmt_api_bp.route('/config/update_request', methods=['POST'])
def submit_update_request():
"""
WMT client sends current device info as an update request for admin approval.
WMT client sends its current running config so the server can validate it.
Three outcomes:
1. Device known AND config matches server record
→ record config_synced_at = now, update last_seen
→ respond {"status": "ok", "in_sync": true}
Client does nothing.
2. Device known BUT config differs from server record
→ the server is authoritative; client must pull fresh config
→ respond {"status": "sync_required", "in_sync": false}
Client should call GET /api/wmt/config/<mac> to obtain correct values.
3. Device unknown (new client not registered by MAC or hostname)
→ create WMTUpdateRequest so admin can approve and assign settings
→ respond {"status": "pending_approval"}
Client waits; it will get real config once admin approves.
card_presence is always applied directly no approval required.
Expected JSON:
{
@@ -133,7 +153,8 @@ def submit_update_request():
"device_name": "Masa-01",
"hostname": "rpi-masa01",
"device_ip": "192.168.1.100",
"client_config_mtime": "2026-04-22T09:30:00" // optional
"card_presence": "enable",
"client_config_mtime": "2026-04-22T09:30:00"
}
"""
if not request.is_json:
@@ -144,31 +165,138 @@ def submit_update_request():
if not mac:
return jsonify({'error': 'mac_address is required'}), 400
proposed_name = (data.get('device_name') or '').strip()
proposed_hostname = (data.get('hostname') or '').strip()
proposed_ip = (data.get('device_ip') or '').strip()
card_presence = data.get('card_presence')
def _eq(a, b):
return (a or '') == (b or '')
try:
with get_db().get_session() as session:
device = session.query(Device).filter_by(mac_address=mac).first()
req = WMTUpdateRequest(
mac_address=mac,
device_id=device.id if device else None,
proposed_device_name=data.get('device_name'),
proposed_hostname=data.get('hostname'),
proposed_device_ip=data.get('device_ip'),
client_config_mtime=data.get('client_config_mtime'),
submitted_at=datetime.utcnow(),
status='pending',
# ── Outcome 3: unknown device ─────────────────────────────
if not device:
# Check if a pending request with the same data already exists
existing = (
session.query(WMTUpdateRequest)
.filter_by(mac_address=mac, status='pending')
.order_by(WMTUpdateRequest.submitted_at.desc())
.first()
)
if existing and (
_eq(existing.proposed_device_name, proposed_name) and
_eq(existing.proposed_hostname, proposed_hostname) and
_eq(existing.proposed_device_ip, proposed_ip)
):
existing.submitted_at = datetime.utcnow()
logger.debug(f'WMT unknown device {mac} refreshed existing pending request')
else:
req = WMTUpdateRequest(
mac_address=mac,
device_id=None,
proposed_device_name=proposed_name or None,
proposed_hostname=proposed_hostname or None,
proposed_device_ip=proposed_ip or None,
client_config_mtime=data.get('client_config_mtime'),
submitted_at=datetime.utcnow(),
status='pending',
)
session.add(req)
logger.info(f'WMT pending approval request created for unknown device {mac}')
return jsonify({
'status': 'pending_approval',
'message': 'Device not registered. Awaiting admin approval.',
}), 202
# Device is known from here on ─────────────────────────────
device.last_seen = datetime.utcnow()
if card_presence in ('enable', 'disable'):
device.card_presence = card_presence
# ── Outcome 1: config matches server record ────────────────
config_in_sync = (
_eq(proposed_name, device.nume_masa) and
_eq(proposed_hostname, device.hostname) and
_eq(proposed_ip, device.device_ip)
)
session.add(req)
# Update device last_seen
if device:
device.last_seen = datetime.utcnow()
# card_presence is a device capability flag update directly (no approval needed)
if data.get('card_presence') in ('enable', 'disable'):
device.card_presence = data['card_presence']
if config_in_sync:
device.config_synced_at = datetime.utcnow()
logger.debug(f'WMT config OK for {mac} synced_at updated')
return jsonify({
'status': 'ok',
'in_sync': True,
'message': 'Config matches server record.',
}), 200
# ── Outcome 2: config differs client must pull from server ─
logger.info(
f'WMT config mismatch for {mac}: '
f'client has name={proposed_name!r} host={proposed_hostname!r} ip={proposed_ip!r} '
f'but server has name={device.nume_masa!r} host={device.hostname!r} ip={device.device_ip!r}'
)
return jsonify({
'status': 'sync_required',
'in_sync': False,
'message': 'Config differs from server record. Pull updated config.',
}), 200
logger.info(f'WMT update request received from {mac}')
return jsonify({'status': 'received', 'message': 'Update request queued for admin review'}), 201
except Exception as e:
logger.error(f'Error saving WMT update request from {mac}: {e}')
logger.error(f'Error processing WMT config check from {mac}: {e}')
return jsonify({'error': str(e)}), 500
# ---------------------------------------------------------------------------
# WMT client auto-update endpoints
# ---------------------------------------------------------------------------
WMT_RELEASES_DIR = Path('data/wmt_releases')
@wmt_api_bp.route('/client/version', methods=['GET'])
def get_client_version():
"""
Returns the latest available WMT client version metadata.
Client calls this to decide if it needs to update.
Response: { "version": "3.0", "notes": "...", "uploaded_at": "...", "filename": "wmt_v3.0.zip" }
"""
meta_path = WMT_RELEASES_DIR / 'latest.json'
if not meta_path.exists():
return jsonify({'error': 'No release available'}), 404
try:
meta = json.loads(meta_path.read_text())
return jsonify(meta), 200
except Exception as e:
logger.error(f'Error reading WMT release metadata: {e}')
return jsonify({'error': str(e)}), 500
@wmt_api_bp.route('/client/download', methods=['GET'])
def download_client_release():
"""
Streams the latest WMT client release zip to the requesting device.
Client downloads this when its version is older than the server version.
"""
meta_path = WMT_RELEASES_DIR / 'latest.json'
if not meta_path.exists():
return jsonify({'error': 'No release available'}), 404
try:
meta = json.loads(meta_path.read_text())
zip_path = WMT_RELEASES_DIR / meta['filename']
if not zip_path.exists():
return jsonify({'error': f'Release file not found: {meta["filename"]}'}), 404
logger.info(f'WMT client downloading release {meta["version"]} from {request.remote_addr}')
return send_file(
str(zip_path.resolve()),
mimetype='application/zip',
as_attachment=True,
download_name=meta['filename'],
)
except Exception as e:
logger.error(f'Error serving WMT release: {e}')
return jsonify({'error': str(e)}), 500