Use Node.js script for Cronicle trigger to avoid shell dependency

Switches from curl (shell not available in Cronicle container) to a Node.js
inline script using the built-in http module. Tries raggr-green first, falls
back to raggr-blue automatically — no manual update needed on color swap.

Delete the existing broken Cronicle event before redeploying.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 08:21:09 -04:00
parent 15a03dba91
commit 504753eef9
3 changed files with 34 additions and 19 deletions
+34 -16
View File
@@ -47,7 +47,35 @@ async def _get_first_category_id() -> str:
return rows[0]["id"]
async def _create_event(title: str, url: str, headers: str, category_id: str) -> str:
def _make_trigger_script(cron_secret: str) -> str:
"""Node.js script that tries raggr-green then falls back to raggr-blue.
Uses Node's built-in http module — no curl/wget needed, and node is always
present since Cronicle itself runs on Node.
"""
path = "/api/scheduled-messages/trigger"
return f"""#!/usr/bin/env node
const http = require('http');
function post(host) {{
return new Promise((resolve, reject) => {{
const req = http.request(
{{ hostname: host, port: 8080, path: '{path}', method: 'POST',
headers: {{'X-Cron-Secret': '{cron_secret}'}} }},
res => {{ res.resume(); resolve(res.statusCode); }}
);
req.setTimeout(10000, () => {{ req.destroy(); reject(new Error('timeout')); }});
req.on('error', reject);
req.end();
}});
}}
post('raggr-green').catch(() => post('raggr-blue')).catch(err => {{
console.error('trigger failed:', err.message);
process.exitCode = 1;
}});
"""
async def _create_event(title: str, script: str, category_id: str) -> str:
async with httpx.AsyncClient() as client:
resp = await client.post(
f"{_base_url()}/api/app/create_event/v1",
@@ -55,17 +83,12 @@ async def _create_event(title: str, url: str, headers: str, category_id: str) ->
"api_key": _api_key(),
"title": title,
"enabled": 1,
"plugin": "urlplug",
"plugin": "shellplug",
"target": "allgrp",
"category": category_id,
# Every minute: all 60 minutes explicitly (Cronicle {} defaults to hourly)
# Every minute: list all 60 explicitly (Cronicle {} defaults to hourly)
"timing": {"minutes": list(range(60))},
"params": {
"method": "post",
"url": url,
"headers": headers,
"data": "",
},
"params": {"script": script},
"notes": "Auto-registered by raggr on startup",
},
timeout=10,
@@ -87,7 +110,6 @@ async def ensure_cronicle_events() -> None:
return
cron_secret = os.environ.get("CRON_SECRET", "")
raggr_url = os.environ.get("RAGGR_INTERNAL_URL", "http://raggr-green:8080")
try:
events = await _get_events()
@@ -96,12 +118,8 @@ async def ensure_cronicle_events() -> None:
return
category_id = await _get_first_category_id()
event_id = await _create_event(
title=_SCHEDULED_MESSAGES_TITLE,
url=f"{raggr_url}/api/scheduled-messages/trigger",
headers=f"X-Cron-Secret: {cron_secret}",
category_id=category_id,
)
script = _make_trigger_script(cron_secret)
event_id = await _create_event(_SCHEDULED_MESSAGES_TITLE, script, category_id)
logger.info(
"Cronicle: created '%s' (id=%s)", _SCHEDULED_MESSAGES_TITLE, event_id
)