File size: 3,801 Bytes
6a6337e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34c461b
 
 
 
6a6337e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
Climate-Resilient Agriculture Platform - Unified Entry Point
Optimized for Hugging Face Spaces and single-container deployment

Starts ALL services:
  1. Pest Prediction API       (Port 8000)
  2. Weather Intelligence API  (Port 8001)
  3. Water Management API      (Port 8002)
  4. Master API Server         (Port 7860/8003)
  5. Telegram Bot              (Background Task)
"""

import os
import sys
import asyncio
import logging
import threading
import time
from pathlib import Path
from dotenv import load_dotenv
import uvicorn

# Load environment variables
load_dotenv()

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger("MasterApp")

# Ensure required directories exist
Path("data").mkdir(exist_ok=True)

# Define services to run
# Format: (module_name, port_number)
SERVICES = [
    ("pest:app", 8000),
    ("weather:app", 8001),
    ("water:app", 8002)
]

def run_service(app_path: str, port: int):
    """Run a sub-service in its own thread"""
    try:
        logger.info(f"[STARTUP] Launching {app_path} on port {port}...")
        uvicorn.run(
            app_path,
            host="127.0.0.1",
            port=port,
            log_level="error", # Keep logs clean
            access_log=False
        )
    except Exception as e:
        logger.error(f"[ERROR] Service {app_path} failed: {e}")

def run_telegram_bot():
    """Runs Telegram bot in its own event loop"""
    try:
        logger.info("[BOT] Initializing Telegram bot thread...")
        
        # Give services time to start up
        time.sleep(5) 

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        from telegram_bot import main as bot_main
        
        # If bot_main is async → await it
        if asyncio.iscoroutinefunction(bot_main):
            loop.run_until_complete(bot_main())
        else:
            bot_main()

    except Exception as e:
        logger.warning(f"[BOT WARNING] Telegram bot failed to initialize: {type(e).__name__}")
        logger.info(f"[BOT INFO] This is normal if there's no internet/network access")
        logger.info(f"[BOT INFO] REST APIs will continue working without the bot")
        # Don't crash the app - just continue without the bot

def run_app():
    """Starts the full platform"""
    logger.info("=" * 80)
    logger.info("🌾 FarmSense MASTER STARTUP")
    logger.info("=" * 80)

    # 1. Start sub-services in background threads
    for app_path, port in SERVICES:
        t = threading.Thread(target=run_service, args=(app_path, port), daemon=True)
        t.start()
        logger.info(f"[STARTUP] {app_path} started in background")

    # 2. Wait a moment for services to bind
    time.sleep(2)

    # 3. Start Telegram bot in background thread
    bot_thread = threading.Thread(
        target=run_telegram_bot,
        name="TelegramBotThread",
        daemon=True
    )
    bot_thread.start()
    logger.info("[STARTUP] Telegram Bot started in background")

    # 4. Run Main Master API on port 7860 (Hugging Face Default)
    # This remains on the main thread
    main_port = int(os.getenv("PORT", 7860))
    logger.info(f"[STARTUP] Master API starting on port {main_port}...")
    
    uvicorn.run(
        "api_server:app",
        host="0.0.0.0",
        port=main_port,
        log_level="info",
        access_log=True,
        reload=False
    )

if __name__ == "__main__":
    try:
        run_app()
    except KeyboardInterrupt:
        logger.info("[SHUTDOWN] Graceful shutdown initiated")
    except Exception as e:
        logger.exception(f"[FATAL ERROR] {e}")
        sys.exit(1)
    finally:
        logger.info("[EXIT] Application stopped")