2 Commits

7 changed files with 49 additions and 6 deletions

View File

@@ -46,9 +46,8 @@ import re as _re
# /api/lotto 만 골라내기 위한 정규식. business log (source='log') 는 모두 통과. # /api/lotto 만 골라내기 위한 정규식. business log (source='log') 는 모두 통과.
AGENT_CONTAINER_MAP: dict[str, tuple[str, int, _re.Pattern]] = { AGENT_CONTAINER_MAP: dict[str, tuple[str, int, _re.Pattern]] = {
"lotto": ("lotto", 8000, _re.compile(r"^/api/lotto")), "lotto": ("lotto", 8000, _re.compile(r"^/api/lotto")),
# Phase 2 에서 추가: "stock": ("stock", 8000, _re.compile(r"^/api/(stock|trade|portfolio)")),
# "stock": ("stock", 8000, _re.compile(r"^/api/(stock|trade|portfolio)")), "music": ("music-lab", 8000, _re.compile(r"^/api/music")),
# "music": ("music-lab", 8000, _re.compile(r"^/api/music")), "insta": ("insta-lab", 8000, _re.compile(r"^/api/insta")),
# "insta": ("insta-lab", 8000, _re.compile(r"^/api/insta")), "realestate": ("realestate-lab", 8000, _re.compile(r"^/api/realestate")),
# "realestate": ("realestate-lab", 8000, _re.compile(r"^/api/realestate")),
} }

View File

@@ -1,7 +1,9 @@
import asyncio import asyncio
import logging
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from .agents import AGENT_REGISTRY from .agents import AGENT_REGISTRY
from .db import delete_old_logs
scheduler = AsyncIOScheduler(timezone="Asia/Seoul") scheduler = AsyncIOScheduler(timezone="Asia/Seoul")
@@ -81,6 +83,11 @@ async def _poll_pipelines():
if agent: if agent:
await agent.poll_state_changes() await agent.poll_state_changes()
def _cleanup_old_logs():
n = delete_old_logs(days=90)
if n:
logging.getLogger(__name__).info("delete_old_logs: %d rows removed", n)
def init_scheduler(): def init_scheduler():
scheduler.add_job(_run_stock_schedule, "cron", hour=7, minute=30, id="stock_news") scheduler.add_job(_run_stock_schedule, "cron", hour=7, minute=30, id="stock_news")
scheduler.add_job( scheduler.add_job(
@@ -117,4 +124,5 @@ def init_scheduler():
scheduler.add_job(_run_youtube_research, "cron", hour=9, minute=10, id="youtube_research") scheduler.add_job(_run_youtube_research, "cron", hour=9, minute=10, id="youtube_research")
scheduler.add_job(_send_youtube_weekly_report, "cron", day_of_week="mon", hour=8, minute=0, id="youtube_weekly_report") scheduler.add_job(_send_youtube_weekly_report, "cron", day_of_week="mon", hour=8, minute=0, id="youtube_weekly_report")
scheduler.add_job(_poll_pipelines, "interval", seconds=30, id="pipeline_poll") scheduler.add_job(_poll_pipelines, "interval", seconds=30, id="pipeline_poll")
scheduler.add_job(_cleanup_old_logs, "cron", hour=3, minute=0, id="cleanup_old_logs", replace_existing=True)
scheduler.start() scheduler.start()

View File

@@ -51,8 +51,15 @@ services:
- OLLAMA_MODEL=${OLLAMA_MODEL:-qwen3:14b} - OLLAMA_MODEL=${OLLAMA_MODEL:-qwen3:14b}
- CORS_ALLOW_ORIGINS=${CORS_ALLOW_ORIGINS:-http://localhost:3007,http://localhost:8080} - CORS_ALLOW_ORIGINS=${CORS_ALLOW_ORIGINS:-http://localhost:3007,http://localhost:8080}
- WEBAI_API_KEY=${WEBAI_API_KEY:-} - WEBAI_API_KEY=${WEBAI_API_KEY:-}
- PYTHONPATH=/app:/shared
volumes: volumes:
- ${RUNTIME_PATH}/data/stock:/app/data - ${RUNTIME_PATH}/data/stock:/app/data
- ${RUNTIME_PATH}/_shared:/shared/_shared:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
healthcheck: healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
interval: 60s interval: 60s
@@ -86,9 +93,16 @@ services:
- REDIS_URL=${REDIS_URL:-redis://redis:6379} - REDIS_URL=${REDIS_URL:-redis://redis:6379}
- INTERNAL_API_KEY=${INTERNAL_API_KEY:-} - INTERNAL_API_KEY=${INTERNAL_API_KEY:-}
- MUSIC_RENDER_URL=${MUSIC_RENDER_URL:-http://192.168.45.59:18711} - MUSIC_RENDER_URL=${MUSIC_RENDER_URL:-http://192.168.45.59:18711}
- PYTHONPATH=/app:/shared
volumes: volumes:
- ${RUNTIME_PATH}/data/music:/app/data - ${RUNTIME_PATH}/data/music:/app/data
- ${RUNTIME_PATH:-.}/data/videos:/app/data/videos - ${RUNTIME_PATH:-.}/data/videos:/app/data/videos
- ${RUNTIME_PATH}/_shared:/shared/_shared:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
depends_on: depends_on:
- redis - redis
healthcheck: healthcheck:
@@ -163,8 +177,15 @@ services:
- CORS_ALLOW_ORIGINS=${CORS_ALLOW_ORIGINS:-http://localhost:3007,http://localhost:8080} - CORS_ALLOW_ORIGINS=${CORS_ALLOW_ORIGINS:-http://localhost:3007,http://localhost:8080}
- REDIS_URL=${REDIS_URL:-redis://redis:6379} - REDIS_URL=${REDIS_URL:-redis://redis:6379}
- INTERNAL_API_KEY=${INTERNAL_API_KEY:-} - INTERNAL_API_KEY=${INTERNAL_API_KEY:-}
- PYTHONPATH=/app:/shared
volumes: volumes:
- ${RUNTIME_PATH}/data/insta:/app/data - ${RUNTIME_PATH}/data/insta:/app/data
- ${RUNTIME_PATH}/_shared:/shared/_shared:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
depends_on: depends_on:
- redis - redis
healthcheck: healthcheck:
@@ -185,8 +206,15 @@ services:
- DATA_GO_KR_API_KEY=${DATA_GO_KR_API_KEY:-} - DATA_GO_KR_API_KEY=${DATA_GO_KR_API_KEY:-}
- CORS_ALLOW_ORIGINS=${CORS_ALLOW_ORIGINS:-http://localhost:3007,http://localhost:8080} - CORS_ALLOW_ORIGINS=${CORS_ALLOW_ORIGINS:-http://localhost:3007,http://localhost:8080}
- AGENT_OFFICE_URL=${AGENT_OFFICE_URL:-http://agent-office:8000} - AGENT_OFFICE_URL=${AGENT_OFFICE_URL:-http://agent-office:8000}
- PYTHONPATH=/app:/shared
volumes: volumes:
- ${RUNTIME_PATH}/data/realestate:/app/data - ${RUNTIME_PATH}/data/realestate:/app/data
- ${RUNTIME_PATH}/_shared:/shared/_shared:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
healthcheck: healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
interval: 60s interval: 60s

View File

@@ -10,6 +10,7 @@ from fastapi import FastAPI, HTTPException, BackgroundTasks, Body, Query
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from pydantic import BaseModel from pydantic import BaseModel
from _shared.access_log import install as install_access_log
from .config import ( from .config import (
CORS_ALLOW_ORIGINS, NAVER_CLIENT_ID, ANTHROPIC_API_KEY, CORS_ALLOW_ORIGINS, NAVER_CLIENT_ID, ANTHROPIC_API_KEY,
@@ -27,6 +28,7 @@ REDIS_URL = os.getenv("REDIS_URL", "redis://redis:6379")
redis_client = aioredis.from_url(REDIS_URL, decode_responses=False) redis_client = aioredis.from_url(REDIS_URL, decode_responses=False)
app = FastAPI() app = FastAPI()
install_access_log(app)
app.include_router(internal_router) app.include_router(internal_router)
app.add_middleware( app.add_middleware(

View File

@@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional
from fastapi import FastAPI, HTTPException, BackgroundTasks, Query from fastapi import FastAPI, HTTPException, BackgroundTasks, Query
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel from pydantic import BaseModel
from _shared.access_log import install as install_access_log
from .db import ( from .db import (
init_db, init_db,
@@ -34,6 +35,7 @@ import redis.asyncio as aioredis
from .internal_router import router as internal_router from .internal_router import router as internal_router
app = FastAPI() app = FastAPI()
install_access_log(app)
REDIS_URL = os.getenv("REDIS_URL", "redis://redis:6379") REDIS_URL = os.getenv("REDIS_URL", "redis://redis:6379")
redis_client = aioredis.from_url(REDIS_URL, decode_responses=False) redis_client = aioredis.from_url(REDIS_URL, decode_responses=False)

View File

@@ -6,6 +6,7 @@ from contextlib import asynccontextmanager
from fastapi import BackgroundTasks, FastAPI, Query, HTTPException from fastapi import BackgroundTasks, FastAPI, Query, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from _shared.access_log import install as install_access_log
from .db import ( from .db import (
init_db, get_announcements, get_announcement, create_announcement, init_db, get_announcements, get_announcement, create_announcement,
@@ -68,6 +69,7 @@ async def lifespan(app: FastAPI):
app = FastAPI(lifespan=lifespan) app = FastAPI(lifespan=lifespan)
install_access_log(app)
_cors_origins = os.getenv("CORS_ALLOW_ORIGINS", "http://localhost:3007,http://localhost:8080").split(",") _cors_origins = os.getenv("CORS_ALLOW_ORIGINS", "http://localhost:3007,http://localhost:8080").split(",")
app.add_middleware( app.add_middleware(

View File

@@ -9,6 +9,7 @@ from fastapi.middleware.cors import CORSMiddleware
import requests import requests
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from pydantic import BaseModel from pydantic import BaseModel
from _shared.access_log import install as install_access_log
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s %(message)s") logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s %(message)s")
logger = logging.getLogger("stock") logger = logging.getLogger("stock")
@@ -28,6 +29,7 @@ from .auth import verify_webai_key
from . import webai_cache from . import webai_cache
app = FastAPI() app = FastAPI()
install_access_log(app)
# Screener 라우터 등록 # Screener 라우터 등록
from app.screener.router import router as screener_router from app.screener.router import router as screener_router