import os
import secrets
import json
import uuid
import logging
from typing import Optional, List
from fastapi import HTTPException
from sqlalchemy.orm import Session
import jinja2
from app.db import models
from app.api import schemas
logger = logging.getLogger(__name__)
class MeshService:
def __init__(self, services=None):
self.services = services
# Setup Jinja2 templates
self.templates_dir = os.path.join(os.path.dirname(__file__), "..", "templates", "provisioning")
self.jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(self.templates_dir)) if os.path.exists(self.templates_dir) else None
# Extracted from nodes.py
def require_node_access(self, user_id: str, node_id: str, db: Session):
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found.")
if user.role == "admin":
return user
access = db.query(models.NodeGroupAccess).filter(
models.NodeGroupAccess.node_id == node_id,
models.NodeGroupAccess.group_id == user.group_id
).first()
if access:
return user
if user.group and user.group.policy:
policy_nodes = user.group.policy.get("nodes", [])
if isinstance(policy_nodes, list) and node_id in policy_nodes:
return user
raise HTTPException(status_code=403, detail=f"Access Denied: You do not have permission to access node '{node_id}'.")
def node_to_user_view(self, node: models.AgentNode, registry) -> schemas.AgentNodeUserView:
live = registry.get_node(node.node_id)
status = live._compute_status() if live else "offline"
skill_cfg = node.skill_config or {}
if isinstance(skill_cfg, str):
try: skill_cfg = json.loads(skill_cfg)
except: skill_cfg = {}
available = [skill for skill, cfg in skill_cfg.items() if isinstance(cfg, dict) and cfg.get("enabled", True)]
stats = live.stats if live else {}
return schemas.AgentNodeUserView(
node_id=node.node_id,
display_name=node.display_name,
description=node.description,
capabilities=node.capabilities or {},
available_skills=available,
last_status=status,
last_seen_at=node.last_seen_at,
stats=schemas.AgentNodeStats(**stats) if stats else schemas.AgentNodeStats()
)
def generate_provisioning_script(self, node: models.AgentNode, config_yaml: str, base_url: str) -> str:
if not self.jinja_env:
return "Error: Templates directory not found."
try:
template = self.jinja_env.get_template("provision.py.j2")
return template.render(
node_id=node.node_id,
config_yaml=config_yaml,
base_url=base_url,
invite_token=node.invite_token
)
except Exception as e:
logger.error(f"Failed to generate provisioning script: {e}")
return f"Error: {e}"
def get_template_content(self, filename: str) -> str:
if not self.jinja_env:
return ""
try:
return self.jinja_env.get_template(filename).render()
except:
return ""