mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 05:24:49 +09:00
Lets a panel link itself to a master ServerKit panel as a worker WITHOUT installing the standalone Go agent: the panel registers via the master's existing token flow, then runs an embedded agent client (HMAC auth + HTTP long-poll transport) that dispatches agent commands (file:write, docker:compose:up/ps/logs, system:info/metrics) onto its own local services. The master needs zero changes — a linked panel is just another agent-backed Server row with scoped, revocable credentials. - linked_panel_config table (migration 077) + Fernet-encrypted secret - embedded agent client: connect/poll/result loop, heartbeat metrics, per-command threads, allowed-roots guard on file:write - API: GET/POST/DELETE /api/v1/linked-panel (+ startup resume hook) - Servers page: LinkedPanelCard (link form, live status, unlink)
46 lines
1.5 KiB
Python
46 lines
1.5 KiB
Python
"""Linked-panel API — manage this panel's link to a master ServerKit panel."""
|
|
from flask import Blueprint, jsonify, request
|
|
from flask_jwt_extended import jwt_required
|
|
|
|
from app.middleware.rbac import developer_required
|
|
from app.services.linked_panel_service import LinkedPanelService
|
|
|
|
linked_panel_bp = Blueprint('linked_panel', __name__)
|
|
|
|
|
|
@linked_panel_bp.route('', methods=['GET'])
|
|
@jwt_required()
|
|
def get_status():
|
|
"""Current link status (linked/connected/last_error)."""
|
|
return jsonify(LinkedPanelService.get_status())
|
|
|
|
|
|
@linked_panel_bp.route('', methods=['POST'])
|
|
@jwt_required()
|
|
@developer_required
|
|
def link():
|
|
"""Link this panel to a master ServerKit panel.
|
|
|
|
Body: {"master_url": "https://panel-a", "registration_token": "sk_reg_…",
|
|
"name": "optional display name"}
|
|
The registration token comes from the master's Servers page (Add Server
|
|
/ regenerate-token) — the same token the Go agent consumes.
|
|
"""
|
|
data = request.get_json(silent=True) or {}
|
|
result = LinkedPanelService.link(
|
|
master_url=data.get('master_url'),
|
|
registration_token=data.get('registration_token'),
|
|
name=data.get('name'),
|
|
)
|
|
if not result.get('success'):
|
|
return jsonify(result), 400
|
|
return jsonify(result)
|
|
|
|
|
|
@linked_panel_bp.route('', methods=['DELETE'])
|
|
@jwt_required()
|
|
@developer_required
|
|
def unlink():
|
|
"""Stop the embedded agent and forget the master credentials."""
|
|
return jsonify(LinkedPanelService.unlink())
|