2026-05-09 22:18:40 +02:00
|
|
|
import strawberry
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
import os
|
2026-05-11 15:05:29 +02:00
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
2026-05-09 22:18:40 +02:00
|
|
|
from api.crontab_manager import CrontabManager
|
2026-05-19 15:15:05 +02:00
|
|
|
from common import PID_FILE
|
2026-05-09 22:18:40 +02:00
|
|
|
|
2026-05-11 15:05:29 +02:00
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
def get_manager():
|
|
|
|
|
tabfile = os.getenv("TABFILE")
|
|
|
|
|
return CrontabManager(tabfile=tabfile)
|
|
|
|
|
|
2026-05-11 15:05:29 +02:00
|
|
|
|
2026-05-19 15:15:05 +02:00
|
|
|
def is_wecker_ringing() -> bool:
|
|
|
|
|
"""Check if the wecker process is currently running via the PID file."""
|
|
|
|
|
if not os.path.exists(PID_FILE):
|
|
|
|
|
return False
|
|
|
|
|
try:
|
|
|
|
|
pid: int
|
|
|
|
|
with open(PID_FILE) as f:
|
|
|
|
|
pid = int(f.read().strip())
|
|
|
|
|
os.kill(pid, 0)
|
|
|
|
|
return True
|
|
|
|
|
except (ValueError, ProcessLookupError, PermissionError, OSError):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
@strawberry.type
|
|
|
|
|
class Alarm:
|
|
|
|
|
id: str
|
|
|
|
|
cron_expression: str
|
|
|
|
|
command: str
|
|
|
|
|
is_enabled: bool
|
|
|
|
|
|
2026-05-11 15:05:29 +02:00
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
@strawberry.type
|
|
|
|
|
class Query:
|
2026-05-19 15:15:05 +02:00
|
|
|
@strawberry.field
|
|
|
|
|
def is_ringing(self) -> bool:
|
|
|
|
|
return is_wecker_ringing()
|
|
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
@strawberry.field
|
|
|
|
|
def get_alarms(self) -> List[Alarm]:
|
|
|
|
|
manager = get_manager()
|
|
|
|
|
return [Alarm(**a) for a in manager.get_alarms()]
|
|
|
|
|
|
|
|
|
|
@strawberry.field
|
|
|
|
|
def get_alarm(self, id: str) -> Optional[Alarm]:
|
|
|
|
|
manager = get_manager()
|
|
|
|
|
alarms = manager.get_alarms()
|
|
|
|
|
for a in alarms:
|
|
|
|
|
if a["id"] == id:
|
|
|
|
|
return Alarm(**a)
|
|
|
|
|
return None
|
|
|
|
|
|
2026-05-11 15:05:29 +02:00
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
@strawberry.type
|
|
|
|
|
class Mutation:
|
|
|
|
|
@strawberry.field
|
|
|
|
|
def set_alarm(
|
|
|
|
|
self,
|
|
|
|
|
cron_expression: str,
|
2026-05-11 15:05:29 +02:00
|
|
|
command: Optional[str] = None,
|
2026-05-09 22:18:40 +02:00
|
|
|
is_enabled: bool = True,
|
2026-05-11 15:05:29 +02:00
|
|
|
id: Optional[str] = None,
|
2026-05-09 22:18:40 +02:00
|
|
|
) -> Alarm:
|
2026-05-11 15:05:29 +02:00
|
|
|
if command is None:
|
|
|
|
|
project_root = Path(__file__).parent.parent.absolute()
|
|
|
|
|
python_exec = sys.executable
|
2026-05-11 22:08:57 +02:00
|
|
|
command = f"cd {project_root} && {python_exec} wecker.py >> wecker.log 2>&1"
|
2026-05-11 15:05:29 +02:00
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
manager = get_manager()
|
|
|
|
|
new_id = manager.set_alarm(
|
|
|
|
|
alarm_id=id,
|
|
|
|
|
cron_expression=cron_expression,
|
|
|
|
|
command=command,
|
2026-05-11 15:05:29 +02:00
|
|
|
is_enabled=is_enabled,
|
2026-05-09 22:18:40 +02:00
|
|
|
)
|
|
|
|
|
return Alarm(
|
|
|
|
|
id=new_id,
|
|
|
|
|
cron_expression=cron_expression,
|
|
|
|
|
command=command,
|
2026-05-11 15:05:29 +02:00
|
|
|
is_enabled=is_enabled,
|
2026-05-09 22:18:40 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@strawberry.field
|
|
|
|
|
def delete_alarm(self, id: str) -> bool:
|
|
|
|
|
manager = get_manager()
|
|
|
|
|
# Verify it exists
|
|
|
|
|
alarms = manager.get_alarms()
|
|
|
|
|
exists = any(a["id"] == id for a in alarms)
|
|
|
|
|
if exists:
|
|
|
|
|
manager.delete_alarm(id)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
2026-05-11 15:05:29 +02:00
|
|
|
|
2026-05-09 22:18:40 +02:00
|
|
|
schema = strawberry.Schema(query=Query, mutation=Mutation)
|