Source code for MAIA.dashboard.core.mongo_sync

import json
from pymongo import MongoClient
from datetime import datetime
import re
import os
from pathlib import Path


[docs] def main(): """ Export project documents from a MongoDB instance into per-project JSON files in a local Projects/ directory. Connects to the database using credentials from environment variables, loads all projects and users, builds a filtered project object for each project (including only users whose namespace lists include the project's namespace and a selected set of metadata fields), normalizes `date` values to `YYYY-MM-DD` when possible, sanitizes the project `namespace` for a safe filename, and writes each filtered project to Projects/<safe_namespace>.json. Raises: ValueError: If the sanitized project namespace is empty or would allow path traversal (unsafe filename). """ db_username = os.environ["DB_USERNAME"] db_password = os.environ["DB_PASS"] db_host = os.environ["DB_HOST"] db_port = os.environ["DB_PORT"] db_name = os.environ["DB_NAME"] client = MongoClient( host=db_host, port=int(db_port), username=db_username, password=db_password, authSource="admin", ) db = client[db_name] collection = db["maia_projects"] user_collection = db["maia_users"] cursor = user_collection.find({}) users = list(cursor) # Fetch all documents cursor = collection.find({}) # Write to file projects = list(cursor) metadata = [ "namespace", "email", "users", "memory_limit", "cpu_limit", "memory_request", "cpu_request", "project_tier", "gpu", "cluster", "date", "supervisor", "description", "auto_deploy", "auto_deploy_apps", "project_configuration", ] project_folder = Path("Projects") project_folder.mkdir(parents=True, exist_ok=True) filtered_table = [] for project in projects: filtered_project = {"users": []} for user in users: user_namespace_value = user.get("namespace") or "" user_namespaces = [namespace.strip() for namespace in user_namespace_value.split(",") if namespace.strip()] if project["namespace"] in user_namespaces: user_email = user.get("email") if user_email: filtered_project["users"].append(user_email) for k, v in project.items(): if k in metadata: if k == "date": if isinstance(v, dict): date_str = v["$date"] try: date_obj = datetime.fromisoformat(date_str.replace("Z", "+00:00")) filtered_project[k] = date_obj.strftime("%Y-%m-%d") except Exception: filtered_project[k] = date_str else: try: if isinstance(v, str): date_obj = datetime.fromisoformat(v.replace("Z", "+00:00")) filtered_project[k] = date_obj.strftime("%Y-%m-%d") elif isinstance(v, datetime): filtered_project[k] = v.strftime("%Y-%m-%d") else: filtered_project[k] = str(v) except Exception: filtered_project[k] = str(v) elif k == "users": pass # users are already filtered else: filtered_project[k] = v # Sanitize the namespace for use as a filename raw_namespace = filtered_project.get("namespace", "") # Allow only lowercase letters, digits, and hyphens in the filename safe_namespace = re.sub(r"[^a-z0-9-]", "_", raw_namespace.lower()) # Additionally, enforce that no path traversal can happen if Path(safe_namespace).name != safe_namespace or not safe_namespace: raise ValueError(f"Unsafe or empty namespace for filename: {raw_namespace}") with open(project_folder.joinpath(safe_namespace + ".json"), "w") as f: json.dump(filtered_project, f, indent=4) filtered_table.append(filtered_project)
if __name__ == "__main__": main()