Files
microdao-daarion/scripts/seed_full_city_reset.py
Apple 8e8f95e9ef feat(db-hardening): Add database persistence, backups, and MinIO assets storage
Database Hardening:
- Add docker-compose.db.yml with persistent PostgreSQL volume
- Add automatic DB backups every 12h (7 days, 4 weeks, 6 months retention)
- Add MinIO S3-compatible storage for assets

Assets Migration:
- Add MinIO client (lib/assets_client.py) for upload/delete
- Update upload endpoint to use MinIO (with local fallback)
- Add migration 043_asset_urls_to_text.sql for full HTTPS URLs
- Simplify normalizeAssetUrl for S3 URLs

Recovery:
- Add seed_full_city_reset.py for emergency city recovery
- Add DB_RESTORE.md with backup restore instructions
- Add SEED_RECOVERY.md with recovery procedures
- Add INFRA_ASSETS_MINIO.md with MinIO setup guide

Task: TASK_PHASE_DATABASE_HARDENING_AND_ASSETS_MIGRATION_v1
2025-12-02 01:56:39 -08:00

291 lines
9.3 KiB
Python

#!/usr/bin/env python3
"""
Emergency recovery script for DAARION City.
Restores essential data after database loss:
- Core MicroDAOs
- City districts
- Basic city rooms
- Core agents for NODE1
- Asset URLs (from MinIO/S3)
"""
import asyncio
import os
import sys
import asyncpg
from typing import List, Dict, Any
# Add parent directory to path for imports
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
DATABASE_URL = os.getenv(
"DATABASE_URL",
"postgresql://postgres:postgres@localhost:5432/daarion"
)
# Base MicroDAOs with S3 asset URLs
BASE_MICRODAO = [
{
"id": "dao_daarion",
"slug": "daarion",
"name": "DAARION DAO",
"description": "Main ecosystem DAO",
"district": "Core",
"is_public": True,
"is_platform": True,
"is_active": True,
"is_pinned": True,
"pinned_weight": 1,
"orchestrator_agent_id": "daarwizz",
"logo_url": "https://assets.daarion.space/daarion-assets/microdao/logo/daarion.png",
"banner_url": None,
},
{
"id": "dao_energy",
"slug": "energy-union",
"name": "Energy Union",
"description": "Energy optimization & sustainability",
"district": "Energy",
"is_public": True,
"is_platform": True,
"is_active": True,
"is_pinned": True,
"pinned_weight": 2,
"orchestrator_agent_id": "helion",
"logo_url": "https://assets.daarion.space/daarion-assets/microdao/logo/energy-union.png",
"banner_url": None,
},
{
"id": "dao_greenfood",
"slug": "greenfood",
"name": "GreenFood DAO",
"description": "Sustainable food systems",
"district": "Green",
"is_public": True,
"is_platform": True,
"is_active": True,
"is_pinned": True,
"pinned_weight": 3,
"orchestrator_agent_id": "greenfood",
"logo_url": "https://assets.daarion.space/daarion-assets/microdao/logo/greenfood.png",
"banner_url": None,
},
{
"id": "dao_soul",
"slug": "soul-retreat",
"name": "Soul Retreat Hub",
"description": "Identity & reputation system",
"district": "Soul",
"is_public": True,
"is_platform": True,
"is_active": True,
"is_pinned": True,
"pinned_weight": 4,
"orchestrator_agent_id": "soul",
"logo_url": "https://assets.daarion.space/daarion-assets/microdao/logo/soul-retreat.png",
"banner_url": None,
},
]
# Core agents for NODE1
CORE_AGENTS_NODE1 = [
{
"id": "daarwizz",
"display_name": "DAARWIZZ",
"kind": "orchestrator",
"role": "Core Orchestrator",
"node_id": "node-1-hetzner-gex44",
"home_node_id": "node-1-hetzner-gex44",
"district": "Core",
"is_public": True,
"is_orchestrator": True,
"status": "online",
},
{
"id": "helion",
"display_name": "Helion",
"kind": "orchestrator",
"role": "Energy Orchestrator",
"node_id": "node-1-hetzner-gex44",
"home_node_id": "node-1-hetzner-gex44",
"district": "Energy",
"is_public": True,
"is_orchestrator": True,
"status": "online",
},
{
"id": "greenfood",
"display_name": "GreenFood Bot",
"kind": "orchestrator",
"role": "Green Orchestrator",
"node_id": "node-1-hetzner-gex44",
"home_node_id": "node-1-hetzner-gex44",
"district": "Green",
"is_public": True,
"is_orchestrator": True,
"status": "online",
},
{
"id": "soul",
"display_name": "Soul Bot",
"kind": "orchestrator",
"role": "Soul Orchestrator",
"node_id": "node-1-hetzner-gex44",
"home_node_id": "node-1-hetzner-gex44",
"district": "Soul",
"is_public": True,
"is_orchestrator": True,
"status": "online",
},
]
async def seed_microdaos(conn: asyncpg.Connection):
"""Seed base MicroDAOs."""
print("📦 Seeding MicroDAOs...")
for dao in BASE_MICRODAO:
await conn.execute("""
INSERT INTO microdao (
id, slug, name, description, district,
is_public, is_platform, is_active, is_pinned, pinned_weight,
orchestrator_agent_id, logo_url, banner_url
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13
)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
district = EXCLUDED.district,
is_public = EXCLUDED.is_public,
is_platform = EXCLUDED.is_platform,
is_active = EXCLUDED.is_active,
is_pinned = EXCLUDED.is_pinned,
pinned_weight = EXCLUDED.pinned_weight,
orchestrator_agent_id = EXCLUDED.orchestrator_agent_id,
logo_url = EXCLUDED.logo_url,
banner_url = EXCLUDED.banner_url,
updated_at = NOW()
""",
dao["id"], dao["slug"], dao["name"], dao["description"], dao["district"],
dao["is_public"], dao["is_platform"], dao["is_active"],
dao["is_pinned"], dao["pinned_weight"], dao["orchestrator_agent_id"],
dao["logo_url"], dao["banner_url"]
)
print(f"{dao['name']}")
print(f"✅ Seeded {len(BASE_MICRODAO)} MicroDAOs")
async def seed_core_agents(conn: asyncpg.Connection):
"""Seed core agents for NODE1."""
print("🤖 Seeding core agents (NODE1)...")
for agent in CORE_AGENTS_NODE1:
await conn.execute("""
INSERT INTO agents (
id, display_name, kind, role, node_id, home_node_id,
district, is_public, is_orchestrator, status,
slug, public_slug, public_title, public_tagline,
public_district, is_listed_in_directory
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
$11, $12, $13, $14, $15, $16
)
ON CONFLICT (id) DO UPDATE SET
display_name = EXCLUDED.display_name,
kind = EXCLUDED.kind,
role = EXCLUDED.role,
node_id = EXCLUDED.node_id,
home_node_id = EXCLUDED.home_node_id,
district = EXCLUDED.district,
is_public = EXCLUDED.is_public,
is_orchestrator = EXCLUDED.is_orchestrator,
status = EXCLUDED.status,
updated_at = NOW()
""",
agent["id"], agent["display_name"], agent["kind"], agent["role"],
agent["node_id"], agent["home_node_id"], agent["district"],
agent["is_public"], agent["is_orchestrator"], agent["status"],
agent["id"], agent["id"], agent["display_name"], agent["role"],
agent["district"], True
)
print(f"{agent['display_name']}")
print(f"✅ Seeded {len(CORE_AGENTS_NODE1)} core agents")
async def link_agents_to_microdaos(conn: asyncpg.Connection):
"""Link orchestrator agents to their MicroDAOs."""
print("🔗 Linking agents to MicroDAOs...")
links = [
("daarwizz", "dao_daarion", True), # is_core
("helion", "dao_energy", True),
("greenfood", "dao_greenfood", True),
("soul", "dao_soul", True),
]
for agent_id, microdao_id, is_core in links:
await conn.execute("""
INSERT INTO microdao_agents (microdao_id, agent_id, is_core, role)
VALUES ($1, $2, $3, 'orchestrator')
ON CONFLICT (microdao_id, agent_id) DO UPDATE SET
is_core = EXCLUDED.is_core,
role = EXCLUDED.role
""", microdao_id, agent_id, is_core)
print(f" ✅ Linked {agent_id}{microdao_id}")
print("✅ Linked agents to MicroDAOs")
async def main():
"""Main recovery function."""
print("=" * 60)
print("🏙️ DAARION City Emergency Recovery")
print("=" * 60)
print()
conn = None
try:
print(f"🔗 Connecting to database...")
conn = await asyncpg.connect(DATABASE_URL)
print("✅ Connected")
print()
# Seed in order
await seed_microdaos(conn)
print()
await seed_core_agents(conn)
print()
await link_agents_to_microdaos(conn)
print()
# Summary
microdao_count = await conn.fetchval("SELECT COUNT(*) FROM microdao")
agent_count = await conn.fetchval("SELECT COUNT(*) FROM agents")
print("=" * 60)
print("✅ Recovery complete!")
print(f" MicroDAOs: {microdao_count}")
print(f" Agents: {agent_count}")
print()
print("📝 Next steps:")
print(" 1. Run migrations if needed")
print(" 2. Run scripts/sync-node2-dagi-agents.py for NODE2 agents")
print(" 3. Upload logos/banners to MinIO if not already done")
print("=" * 60)
except Exception as e:
print(f"❌ Error during recovery: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
finally:
if conn:
await conn.close()
if __name__ == "__main__":
asyncio.run(main())