Merge branch 'master' of https://gitea.tatta.cn/Tatta/TaskPulse
This commit is contained in:
@@ -50,7 +50,7 @@ async def update_agent(agent_id: int, body: AgentUpdate, db: AsyncSession = Depe
|
||||
|
||||
|
||||
@router.post("/{agent_id}/heartbeat", response_model=AgentOut)
|
||||
async def heartbeat(agent_id: int, body: AgentHeartbeat, db: AsyncSession = Depends(get_db)):
|
||||
async def heartbeat(agent_id: int, db: AsyncSession = Depends(get_db)):
|
||||
svc = AgentService(db)
|
||||
agent = await svc.heartbeat(agent_id)
|
||||
if not agent:
|
||||
|
||||
+2
-1
@@ -74,7 +74,8 @@ if FRONTEND_DIST.is_dir():
|
||||
if not request.url.path.startswith("/api"):
|
||||
content = (FRONTEND_DIST / "index.html").read_text(encoding="utf-8")
|
||||
return HTMLResponse(content=content, status_code=200)
|
||||
raise exc
|
||||
from fastapi.responses import JSONResponse
|
||||
return JSONResponse(status_code=404, content={"detail": "Not Found"})
|
||||
|
||||
logger.info("Frontend SPA mounted from %s", FRONTEND_DIST)
|
||||
else:
|
||||
|
||||
@@ -38,6 +38,7 @@ class AgentService:
|
||||
if v is not None and hasattr(agent, k):
|
||||
setattr(agent, k, v)
|
||||
await self.db.flush()
|
||||
await self.db.refresh(agent)
|
||||
return agent
|
||||
|
||||
async def heartbeat(self, agent_id: int) -> Agent | None:
|
||||
@@ -46,6 +47,7 @@ class AgentService:
|
||||
if agent:
|
||||
agent.last_heartbeat_at = datetime.utcnow()
|
||||
await self.db.flush()
|
||||
await self.db.refresh(agent)
|
||||
return agent
|
||||
|
||||
async def get_task_count(self, agent_id: int) -> int:
|
||||
|
||||
@@ -10,6 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.config import settings
|
||||
from app.database import async_session_factory
|
||||
from app.models import ScheduledTask, Alert, AlertRule, NotificationChannel
|
||||
from app.utils import fmt_bj
|
||||
|
||||
logger = logging.getLogger("taskpulse.scheduler")
|
||||
|
||||
@@ -66,9 +67,9 @@ class SchedulerService:
|
||||
alert_type="missed_run",
|
||||
message=(
|
||||
f"任务 [{task.name}](ID={task.id}) 超过预定时间未执行。\n"
|
||||
f"预期执行时间: {task.next_run_at}\n"
|
||||
f"预期执行时间: {fmt_bj(task.next_run_at)}\n"
|
||||
f"容忍窗口: {task.grace_period}s\n"
|
||||
f"当前时间: {now}"
|
||||
f"当前时间: {fmt_bj(now)}"
|
||||
),
|
||||
status="pending",
|
||||
)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Time utility — Beijing time (Asia/Shanghai, UTC+8)."""
|
||||
|
||||
from datetime import datetime, timezone, timedelta, tzinfo
|
||||
|
||||
# Beijing timezone (UTC+8, no DST)
|
||||
_BJ_TZ = timezone(timedelta(hours=8))
|
||||
|
||||
|
||||
def now() -> datetime:
|
||||
"""Return current time in UTC (naive, for DB storage)."""
|
||||
return datetime.utcnow()
|
||||
|
||||
|
||||
def now_bj() -> datetime:
|
||||
"""Return current time as Beijing time (aware)."""
|
||||
return datetime.now(_BJ_TZ)
|
||||
|
||||
|
||||
def fmt_bj(dt: datetime | None) -> str:
|
||||
"""Format a naive datetime (assumed UTC) as Beijing time string.
|
||||
|
||||
Returns e.g. '2026-06-15 10:30:00'
|
||||
"""
|
||||
if dt is None:
|
||||
return "—"
|
||||
# Assume naive dt is UTC
|
||||
utc_dt = dt.replace(tzinfo=timezone.utc)
|
||||
bj_dt = utc_dt.astimezone(_BJ_TZ)
|
||||
return bj_dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
Reference in New Issue
Block a user