mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 13:35:12 +09:00
Enforce admin and workspace permissions, improve logging, and add DNS frontend/API support. - Backend: added admin checks to cloud_provisioning and marketplace endpoints; added require_workspace_access helper and applied to multiple workspace endpoints to enforce membership/admin access; persist push subscription data in mobile API (commit to DB); replaced print() debugging with structured logger calls across agent_registry and added logging/exception handling in monitoring_service. - Frontend: added a DNS API client (frontend/src/services/api/dns.js) and registered it in the API index; extended DNSZones UI to collect provider zone ID and API token, updated create payload and form reset; added optional modal footer prop to Modal component; moved rollup native binding to optionalDependencies in package.json. - Misc: added '*.png' to .gitignore and a minor spacing change in install.sh. These changes improve security checks, observability, and add DNS management features in the UI/API.
123 lines
4.4 KiB
Python
123 lines
4.4 KiB
Python
from flask import Blueprint, request, jsonify
|
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
|
from app.models.user import User
|
|
from app import db
|
|
|
|
mobile_bp = Blueprint('mobile', __name__)
|
|
|
|
|
|
@mobile_bp.route('/push/register', methods=['POST'])
|
|
@jwt_required()
|
|
def register_push():
|
|
"""Register a device for push notifications."""
|
|
user_id = get_jwt_identity()
|
|
data = request.get_json() or {}
|
|
subscription = data.get('subscription')
|
|
device_name = data.get('device_name', 'Unknown')
|
|
|
|
if not subscription:
|
|
return jsonify({'error': 'subscription required'}), 400
|
|
|
|
user = User.query.get(user_id)
|
|
if not user:
|
|
return jsonify({'error': 'User not found'}), 404
|
|
|
|
# Store push subscription in user metadata
|
|
import json
|
|
push_subs = json.loads(user.push_subscriptions_json) if hasattr(user, 'push_subscriptions_json') and user.push_subscriptions_json else []
|
|
# Avoid duplicates
|
|
existing = next((s for s in push_subs if s.get('endpoint') == subscription.get('endpoint')), None)
|
|
if not existing:
|
|
push_subs.append({
|
|
'subscription': subscription,
|
|
'device_name': device_name,
|
|
'registered_at': __import__('datetime').datetime.utcnow().isoformat(),
|
|
})
|
|
|
|
user.push_subscriptions_json = json.dumps(push_subs)
|
|
db.session.commit()
|
|
|
|
return jsonify({'message': 'Device registered', 'device_count': len(push_subs)})
|
|
|
|
|
|
@mobile_bp.route('/push/unregister', methods=['POST'])
|
|
@jwt_required()
|
|
def unregister_push():
|
|
"""Unregister a device from push notifications."""
|
|
data = request.get_json() or {}
|
|
endpoint = data.get('endpoint')
|
|
if not endpoint:
|
|
return jsonify({'error': 'endpoint required'}), 400
|
|
return jsonify({'message': 'Device unregistered'})
|
|
|
|
|
|
@mobile_bp.route('/quick-actions', methods=['GET'])
|
|
@jwt_required()
|
|
def get_quick_actions():
|
|
"""Get available quick actions for mobile."""
|
|
return jsonify({'actions': [
|
|
{'id': 'restart_service', 'label': 'Restart Service', 'icon': 'refresh', 'params': ['service_name']},
|
|
{'id': 'view_stats', 'label': 'View Server Stats', 'icon': 'chart', 'params': []},
|
|
{'id': 'acknowledge_alert', 'label': 'Acknowledge Alert', 'icon': 'check', 'params': ['alert_id']},
|
|
{'id': 'run_backup', 'label': 'Run Backup', 'icon': 'download', 'params': ['backup_id']},
|
|
{'id': 'toggle_maintenance', 'label': 'Toggle Maintenance', 'icon': 'wrench', 'params': ['component_id']},
|
|
]})
|
|
|
|
|
|
@mobile_bp.route('/quick-actions/<action_id>', methods=['POST'])
|
|
@jwt_required()
|
|
def execute_quick_action(action_id):
|
|
"""Execute a quick action."""
|
|
data = request.get_json() or {}
|
|
|
|
if action_id == 'view_stats':
|
|
from app.services.server_metrics_service import ServerMetricsService
|
|
metrics = ServerMetricsService.get_current_metrics()
|
|
return jsonify({'action': 'view_stats', 'result': metrics})
|
|
|
|
elif action_id == 'acknowledge_alert':
|
|
alert_id = data.get('alert_id')
|
|
return jsonify({'action': 'acknowledge_alert', 'alert_id': alert_id, 'acknowledged': True})
|
|
|
|
return jsonify({'action': action_id, 'status': 'executed'})
|
|
|
|
|
|
@mobile_bp.route('/summary', methods=['GET'])
|
|
@jwt_required()
|
|
def get_mobile_summary():
|
|
"""Get a compact summary for mobile dashboard."""
|
|
try:
|
|
from app.services.server_metrics_service import ServerMetricsService
|
|
metrics = ServerMetricsService.get_current_metrics()
|
|
except Exception:
|
|
metrics = {}
|
|
|
|
from app.models.server import Server
|
|
from app.models.security_alert import SecurityAlert
|
|
|
|
server_count = Server.query.count()
|
|
active_alerts = SecurityAlert.query.filter_by(resolved=False).count() if hasattr(SecurityAlert, 'resolved') else 0
|
|
|
|
return jsonify({
|
|
'metrics': {
|
|
'cpu': metrics.get('cpu_percent', 0),
|
|
'memory': metrics.get('memory_percent', 0),
|
|
'disk': metrics.get('disk_percent', 0),
|
|
},
|
|
'servers': server_count,
|
|
'active_alerts': active_alerts,
|
|
})
|
|
|
|
|
|
@mobile_bp.route('/offline-cache', methods=['GET'])
|
|
@jwt_required()
|
|
def get_offline_data():
|
|
"""Get data for offline caching."""
|
|
from app.models.server import Server
|
|
|
|
servers = Server.query.limit(50).all()
|
|
return jsonify({
|
|
'servers': [{'id': s.id, 'name': s.name, 'hostname': s.hostname, 'status': s.status} for s in servers],
|
|
'cached_at': __import__('datetime').datetime.utcnow().isoformat(),
|
|
})
|