somratpro commited on
Commit
06fe1a1
·
1 Parent(s): 728bb65

feat: replace setup-uptimerobot.sh with a modular cloudflare-keepalive-setup.py for automated health check pinging.

Browse files
CHANGELOG.md CHANGED
@@ -6,4 +6,4 @@
6
  - Added HF Space dashboard, `/health`, `/status`, `/v1/*` proxy, and Telegram webhook proxy.
7
  - Added Cloudflare Worker setup for Telegram Bot API base URL proxying.
8
  - Added private HF Dataset backup and restore for Hermes state.
9
- - Added UptimeRobot monitor setup.
 
6
  - Added HF Space dashboard, `/health`, `/status`, `/v1/*` proxy, and Telegram webhook proxy.
7
  - Added Cloudflare Worker setup for Telegram Bot API base URL proxying.
8
  - Added private HF Dataset backup and restore for Hermes state.
9
+ - Added Cloudflare Keepalive Worker setup for automatic space keep-awake.
Dockerfile CHANGED
@@ -16,13 +16,13 @@ COPY --chown=hermes:hermes start.sh /opt/huggingmess/start.sh
16
  COPY --chown=hermes:hermes health-server.js /opt/huggingmess/health-server.js
17
  COPY --chown=hermes:hermes hermes-sync.py /opt/huggingmess/hermes-sync.py
18
  COPY --chown=hermes:hermes cloudflare-proxy-setup.py /opt/huggingmess/cloudflare-proxy-setup.py
19
- COPY --chown=hermes:hermes setup-uptimerobot.sh /opt/huggingmess/setup-uptimerobot.sh
20
 
21
  RUN chmod +x \
22
  /opt/huggingmess/start.sh \
23
  /opt/huggingmess/hermes-sync.py \
24
  /opt/huggingmess/cloudflare-proxy-setup.py \
25
- /opt/huggingmess/setup-uptimerobot.sh
26
 
27
  ENV HERMES_HOME=/opt/data \
28
  HUGGINGMESS_APP_DIR=/opt/huggingmess \
 
16
  COPY --chown=hermes:hermes health-server.js /opt/huggingmess/health-server.js
17
  COPY --chown=hermes:hermes hermes-sync.py /opt/huggingmess/hermes-sync.py
18
  COPY --chown=hermes:hermes cloudflare-proxy-setup.py /opt/huggingmess/cloudflare-proxy-setup.py
19
+ COPY --chown=hermes:hermes cloudflare-keepalive-setup.py /opt/huggingmess/cloudflare-keepalive-setup.py
20
 
21
  RUN chmod +x \
22
  /opt/huggingmess/start.sh \
23
  /opt/huggingmess/hermes-sync.py \
24
  /opt/huggingmess/cloudflare-proxy-setup.py \
25
+ /opt/huggingmess/cloudflare-keepalive-setup.py
26
 
27
  ENV HERMES_HOME=/opt/data \
28
  HUGGINGMESS_APP_DIR=/opt/huggingmess \
README.md CHANGED
@@ -142,7 +142,7 @@ The manual Worker source is included in `cloudflare-worker.js`.
142
 
143
  ### Keep Awake
144
 
145
- When `CLOUDFLARE_WORKERS_TOKEN` and `SPACE_HOST` are present, HuggingMess creates a scheduled Worker that pings:
146
 
147
  ```text
148
  https://your-space.hf.space/health
@@ -160,8 +160,8 @@ Optional variables:
160
  | :--- | :--- | :--- |
161
  | `CLOUDFLARE_KEEPALIVE_ENABLED` | `true` | Set `false` to skip keep-awake Worker setup |
162
  | `CLOUDFLARE_KEEPALIVE_CRON` | `*/10 * * * *` | Cloudflare cron expression |
163
- | `CLOUDFLARE_KEEPALIVE_URL` | `https://<SPACE_HOST>/health` | URL to ping |
164
- | `CLOUDFLARE_KEEPALIVE_WORKER_NAME` | derived from `SPACE_HOST` | Custom Worker name |
165
 
166
  ## Backup
167
 
 
142
 
143
  ### Keep Awake
144
 
145
+ When `CLOUDFLARE_WORKERS_TOKEN` is present, HuggingMess creates a scheduled Worker that pings your Space's `/health` route. It automatically detects your Space hostname:
146
 
147
  ```text
148
  https://your-space.hf.space/health
 
160
  | :--- | :--- | :--- |
161
  | `CLOUDFLARE_KEEPALIVE_ENABLED` | `true` | Set `false` to skip keep-awake Worker setup |
162
  | `CLOUDFLARE_KEEPALIVE_CRON` | `*/10 * * * *` | Cloudflare cron expression |
163
+ | `CLOUDFLARE_KEEPALIVE_URL` | `https://<auto-detected-space-host>/health` | URL to ping |
164
+ | `CLOUDFLARE_KEEPALIVE_WORKER_NAME` | derived from space host | Custom Worker name |
165
 
166
  ## Backup
167
 
cloudflare-keepalive-setup.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ """Create or reuse a Cloudflare Worker for Space keep-awake."""
5
+
6
+ import json
7
+ import os
8
+ import re
9
+ import sys
10
+ import time
11
+ import urllib.request
12
+ from pathlib import Path
13
+
14
+ API_BASE = "https://api.cloudflare.com/client/v4"
15
+ KEEPALIVE_STATUS_FILE = Path("/tmp/huggingmess-cloudflare-keepalive-status.json")
16
+
17
+
18
+ def cf_request(method: str, path: str, token: str, body: bytes | None = None, content_type: str = "application/json"):
19
+ req = urllib.request.Request(
20
+ f"{API_BASE}{path}",
21
+ data=body,
22
+ method=method,
23
+ headers={"Authorization": f"Bearer {token}", "Content-Type": content_type},
24
+ )
25
+ with urllib.request.urlopen(req, timeout=30) as response:
26
+ payload = json.loads(response.read().decode("utf-8"))
27
+ if not payload.get("success"):
28
+ errors = payload.get("errors") or [{"message": "Unknown Cloudflare API error"}]
29
+ raise RuntimeError(errors[0].get("message", "Unknown Cloudflare API error"))
30
+ return payload["result"]
31
+
32
+
33
+ def slugify(value: str) -> str:
34
+ cleaned = re.sub(r"[^a-z0-9-]+", "-", value.lower()).strip("-")
35
+ cleaned = re.sub(r"-{2,}", "-", cleaned)
36
+ return (cleaned or "huggingmess-proxy")[:63].rstrip("-")
37
+
38
+
39
+ def get_space_host() -> str:
40
+ space_host = os.environ.get("SPACE_HOST", "").strip()
41
+ if space_host:
42
+ return space_host
43
+
44
+ author = os.environ.get("SPACE_AUTHOR_NAME", "").strip()
45
+ repo = os.environ.get("SPACE_REPO_NAME", "").strip()
46
+ if author and repo:
47
+ return f"{author}-{repo}.hf.space".lower()
48
+
49
+ return ""
50
+
51
+
52
+ def derive_keepalive_worker_name() -> str:
53
+ explicit = os.environ.get("CLOUDFLARE_KEEPALIVE_WORKER_NAME", "").strip()
54
+ if explicit:
55
+ return slugify(explicit)
56
+ space_host = get_space_host()
57
+ if space_host:
58
+ return slugify(f"{space_host.replace('.hf.space', '')}-keepalive")
59
+ return "huggingmess-keepalive"
60
+
61
+
62
+ def render_keepalive_worker(target_url: str) -> str:
63
+ return f"""addEventListener("fetch", (event) => {{
64
+ event.respondWith(handleRequest(event.request));
65
+ }});
66
+
67
+ addEventListener("scheduled", (event) => {{
68
+ event.waitUntil(ping("cron"));
69
+ }});
70
+
71
+ const TARGET_URL = {json.dumps(target_url)};
72
+
73
+ async function ping(source) {{
74
+ const startedAt = new Date().toISOString();
75
+ try {{
76
+ const response = await fetch(TARGET_URL, {{
77
+ method: "GET",
78
+ headers: {{
79
+ "user-agent": "HuggingMess Cloudflare KeepAlive",
80
+ "cache-control": "no-cache"
81
+ }},
82
+ cf: {{ cacheTtl: 0, cacheEverything: false }}
83
+ }});
84
+ return {{
85
+ ok: response.ok,
86
+ status: response.status,
87
+ source,
88
+ target: TARGET_URL,
89
+ timestamp: startedAt
90
+ }};
91
+ }} catch (error) {{
92
+ return {{
93
+ ok: false,
94
+ status: 0,
95
+ source,
96
+ target: TARGET_URL,
97
+ timestamp: startedAt,
98
+ error: error.message
99
+ }};
100
+ }}
101
+ }}
102
+
103
+ async function handleRequest(request) {{
104
+ const url = new URL(request.url);
105
+ if (url.pathname === "/" || url.pathname === "/health" || url.pathname === "/ping") {{
106
+ const result = await ping("manual");
107
+ return new Response(JSON.stringify(result, null, 2), {{
108
+ status: result.ok ? 200 : 502,
109
+ headers: {{ "content-type": "application/json; charset=utf-8" }}
110
+ }});
111
+ }}
112
+ return new Response("Not found", {{ status: 404 }});
113
+ }}
114
+ """
115
+
116
+
117
+ def write_keepalive_status(payload: dict) -> None:
118
+ payload = {
119
+ **payload,
120
+ "timestamp": payload.get("timestamp") or time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
121
+ }
122
+ KEEPALIVE_STATUS_FILE.write_text(json.dumps(payload), encoding="utf-8")
123
+ try:
124
+ KEEPALIVE_STATUS_FILE.chmod(0o600)
125
+ except OSError:
126
+ pass
127
+
128
+
129
+ def resolve_account_and_subdomain(api_token: str) -> tuple[str, str]:
130
+ account_id = os.environ.get("CLOUDFLARE_ACCOUNT_ID", "").strip()
131
+ if not account_id:
132
+ accounts = cf_request("GET", "/accounts", api_token)
133
+ if not accounts:
134
+ raise RuntimeError("No Cloudflare account is available for this token.")
135
+ account_id = accounts[0]["id"]
136
+
137
+ subdomain_info = cf_request("GET", f"/accounts/{account_id}/workers/subdomain", api_token)
138
+ subdomain = (subdomain_info or {}).get("subdomain", "").strip()
139
+ if not subdomain:
140
+ raise RuntimeError("Cloudflare Workers subdomain is not configured. Enable workers.dev first.")
141
+ return account_id, subdomain
142
+
143
+
144
+ def setup_keepalive_worker(api_token: str, account_id: str, subdomain: str) -> None:
145
+ enabled = os.environ.get("CLOUDFLARE_KEEPALIVE_ENABLED", "true").strip().lower()
146
+ if enabled in {"0", "false", "no", "off"}:
147
+ write_keepalive_status({"configured": False, "status": "disabled", "message": "Cloudflare keep-awake is disabled."})
148
+ return
149
+
150
+ space_host = get_space_host()
151
+ if not space_host:
152
+ write_keepalive_status({"configured": False, "status": "skipped", "message": "SPACE_HOST could not be determined."})
153
+ return
154
+
155
+ cron = os.environ.get("CLOUDFLARE_KEEPALIVE_CRON", "*/10 * * * *").strip()
156
+ space_host = space_host.removeprefix("https://").removeprefix("http://").split("/")[0]
157
+ target_url = os.environ.get("CLOUDFLARE_KEEPALIVE_URL", f"https://{space_host}/health").strip()
158
+ worker_name = derive_keepalive_worker_name()
159
+ worker_source = render_keepalive_worker(target_url)
160
+
161
+ cf_request(
162
+ "PUT",
163
+ f"/accounts/{account_id}/workers/scripts/{worker_name}",
164
+ api_token,
165
+ body=worker_source.encode("utf-8"),
166
+ content_type="application/javascript",
167
+ )
168
+ cf_request(
169
+ "POST",
170
+ f"/accounts/{account_id}/workers/scripts/{worker_name}/subdomain",
171
+ api_token,
172
+ body=json.dumps({"enabled": True, "previews_enabled": True}).encode("utf-8"),
173
+ )
174
+ cf_request(
175
+ "PUT",
176
+ f"/accounts/{account_id}/workers/scripts/{worker_name}/schedules",
177
+ api_token,
178
+ body=json.dumps([{"cron": cron}]).encode("utf-8"),
179
+ )
180
+
181
+ worker_url = f"https://{worker_name}.{subdomain}.workers.dev"
182
+ write_keepalive_status(
183
+ {
184
+ "configured": True,
185
+ "status": "configured",
186
+ "workerName": worker_name,
187
+ "workerUrl": worker_url,
188
+ "targetUrl": target_url,
189
+ "cron": cron,
190
+ "message": f"Cloudflare Worker cron pings {target_url} on {cron}.",
191
+ }
192
+ )
193
+
194
+
195
+ def main() -> int:
196
+ api_token = os.environ.get("CLOUDFLARE_WORKERS_TOKEN", "").strip()
197
+
198
+ if not api_token:
199
+ return 0
200
+
201
+ try:
202
+ account_id, subdomain = resolve_account_and_subdomain(api_token)
203
+ setup_keepalive_worker(api_token, account_id, subdomain)
204
+ return 0
205
+ except Exception as exc:
206
+ print(f"Cloudflare keepalive setup failed: {exc}", file=sys.stderr)
207
+ write_keepalive_status({"configured": False, "status": "error", "message": str(exc)})
208
+ return 1
209
+
210
+
211
+ if __name__ == "__main__":
212
+ raise SystemExit(main())
cloudflare-proxy-setup.py CHANGED
@@ -14,7 +14,7 @@ from pathlib import Path
14
 
15
  API_BASE = "https://api.cloudflare.com/client/v4"
16
  ENV_FILE = Path("/tmp/huggingmess-cloudflare-proxy.env")
17
- KEEPALIVE_STATUS_FILE = Path("/tmp/huggingmess-cloudflare-keepalive-status.json")
18
  DEFAULT_ALLOWED = [
19
  "api.telegram.org",
20
  "discord.com",
@@ -65,16 +65,6 @@ def derive_worker_name() -> str:
65
  return "huggingmess-proxy"
66
 
67
 
68
- def derive_keepalive_worker_name() -> str:
69
- explicit = os.environ.get("CLOUDFLARE_KEEPALIVE_WORKER_NAME", "").strip()
70
- if explicit:
71
- return slugify(explicit)
72
- space_host = os.environ.get("SPACE_HOST", "").strip()
73
- if space_host:
74
- return slugify(f"{space_host.replace('.hf.space', '')}-keepalive")
75
- return "huggingmess-keepalive"
76
-
77
-
78
  def render_worker(secret_value: str, allowed_targets: list[str], allow_proxy_all: bool) -> str:
79
  return f"""addEventListener("fetch", (event) => {{
80
  event.respondWith(handleRequest(event.request));
@@ -141,61 +131,6 @@ async function handleRequest(request) {{
141
  """
142
 
143
 
144
- def render_keepalive_worker(target_url: str) -> str:
145
- return f"""addEventListener("fetch", (event) => {{
146
- event.respondWith(handleRequest(event.request));
147
- }});
148
-
149
- addEventListener("scheduled", (event) => {{
150
- event.waitUntil(ping("cron"));
151
- }});
152
-
153
- const TARGET_URL = {json.dumps(target_url)};
154
-
155
- async function ping(source) {{
156
- const startedAt = new Date().toISOString();
157
- try {{
158
- const response = await fetch(TARGET_URL, {{
159
- method: "GET",
160
- headers: {{
161
- "user-agent": "HuggingMess Cloudflare KeepAlive",
162
- "cache-control": "no-cache"
163
- }},
164
- cf: {{ cacheTtl: 0, cacheEverything: false }}
165
- }});
166
- return {{
167
- ok: response.ok,
168
- status: response.status,
169
- source,
170
- target: TARGET_URL,
171
- timestamp: startedAt
172
- }};
173
- }} catch (error) {{
174
- return {{
175
- ok: false,
176
- status: 0,
177
- source,
178
- target: TARGET_URL,
179
- timestamp: startedAt,
180
- error: error.message
181
- }};
182
- }}
183
- }}
184
-
185
- async function handleRequest(request) {{
186
- const url = new URL(request.url);
187
- if (url.pathname === "/" || url.pathname === "/health" || url.pathname === "/ping") {{
188
- const result = await ping("manual");
189
- return new Response(JSON.stringify(result, null, 2), {{
190
- status: result.ok ? 200 : 502,
191
- headers: {{ "content-type": "application/json; charset=utf-8" }}
192
- }});
193
- }}
194
- return new Response("Not found", {{ status: 404 }});
195
- }}
196
- """
197
-
198
-
199
  def write_env(proxy_url: str, proxy_secret: str) -> None:
200
  ENV_FILE.write_text(
201
  f'export CLOUDFLARE_PROXY_URL="{proxy_url}"\nexport CLOUDFLARE_PROXY_SECRET="{proxy_secret}"\n',
@@ -204,18 +139,6 @@ def write_env(proxy_url: str, proxy_secret: str) -> None:
204
  ENV_FILE.chmod(0o600)
205
 
206
 
207
- def write_keepalive_status(payload: dict) -> None:
208
- payload = {
209
- **payload,
210
- "timestamp": payload.get("timestamp") or time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
211
- }
212
- KEEPALIVE_STATUS_FILE.write_text(json.dumps(payload), encoding="utf-8")
213
- try:
214
- KEEPALIVE_STATUS_FILE.chmod(0o600)
215
- except OSError:
216
- pass
217
-
218
-
219
  def resolve_account_and_subdomain(api_token: str) -> tuple[str, str]:
220
  account_id = os.environ.get("CLOUDFLARE_ACCOUNT_ID", "").strip()
221
  if not account_id:
@@ -231,57 +154,6 @@ def resolve_account_and_subdomain(api_token: str) -> tuple[str, str]:
231
  return account_id, subdomain
232
 
233
 
234
- def setup_keepalive_worker(api_token: str, account_id: str, subdomain: str) -> None:
235
- enabled = os.environ.get("CLOUDFLARE_KEEPALIVE_ENABLED", "true").strip().lower()
236
- if enabled in {"0", "false", "no", "off"}:
237
- write_keepalive_status({"configured": False, "status": "disabled", "message": "Cloudflare keep-awake is disabled."})
238
- return
239
-
240
- space_host = os.environ.get("SPACE_HOST", "").strip()
241
- if not space_host:
242
- write_keepalive_status({"configured": False, "status": "skipped", "message": "SPACE_HOST is not set."})
243
- return
244
-
245
- cron = os.environ.get("CLOUDFLARE_KEEPALIVE_CRON", "*/10 * * * *").strip()
246
- space_host = space_host.removeprefix("https://").removeprefix("http://").split("/")[0]
247
- target_url = os.environ.get("CLOUDFLARE_KEEPALIVE_URL", f"https://{space_host}/health").strip()
248
- worker_name = derive_keepalive_worker_name()
249
- worker_source = render_keepalive_worker(target_url)
250
-
251
- cf_request(
252
- "PUT",
253
- f"/accounts/{account_id}/workers/scripts/{worker_name}",
254
- api_token,
255
- body=worker_source.encode("utf-8"),
256
- content_type="application/javascript",
257
- )
258
- cf_request(
259
- "POST",
260
- f"/accounts/{account_id}/workers/scripts/{worker_name}/subdomain",
261
- api_token,
262
- body=json.dumps({"enabled": True, "previews_enabled": True}).encode("utf-8"),
263
- )
264
- cf_request(
265
- "PUT",
266
- f"/accounts/{account_id}/workers/scripts/{worker_name}/schedules",
267
- api_token,
268
- body=json.dumps([{"cron": cron}]).encode("utf-8"),
269
- )
270
-
271
- worker_url = f"https://{worker_name}.{subdomain}.workers.dev"
272
- write_keepalive_status(
273
- {
274
- "configured": True,
275
- "status": "configured",
276
- "workerName": worker_name,
277
- "workerUrl": worker_url,
278
- "targetUrl": target_url,
279
- "cron": cron,
280
- "message": f"Cloudflare Worker cron pings {target_url} on {cron}.",
281
- }
282
- )
283
-
284
-
285
  def main() -> int:
286
  existing_url = os.environ.get("CLOUDFLARE_PROXY_URL", "").strip()
287
  existing_secret = os.environ.get("CLOUDFLARE_PROXY_SECRET", "").strip()
@@ -319,11 +191,9 @@ def main() -> int:
319
  )
320
  write_env(f"https://{worker_name}.{subdomain}.workers.dev", proxy_secret)
321
 
322
- setup_keepalive_worker(api_token, account_id, subdomain)
323
  return 0
324
  except Exception as exc:
325
  print(f"Cloudflare proxy setup failed: {exc}", file=sys.stderr)
326
- write_keepalive_status({"configured": False, "status": "error", "message": str(exc)})
327
  return 1
328
 
329
 
 
14
 
15
  API_BASE = "https://api.cloudflare.com/client/v4"
16
  ENV_FILE = Path("/tmp/huggingmess-cloudflare-proxy.env")
17
+ ENV_FILE = Path("/tmp/huggingmess-cloudflare-proxy.env")
18
  DEFAULT_ALLOWED = [
19
  "api.telegram.org",
20
  "discord.com",
 
65
  return "huggingmess-proxy"
66
 
67
 
 
 
 
 
 
 
 
 
 
 
68
  def render_worker(secret_value: str, allowed_targets: list[str], allow_proxy_all: bool) -> str:
69
  return f"""addEventListener("fetch", (event) => {{
70
  event.respondWith(handleRequest(event.request));
 
131
  """
132
 
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  def write_env(proxy_url: str, proxy_secret: str) -> None:
135
  ENV_FILE.write_text(
136
  f'export CLOUDFLARE_PROXY_URL="{proxy_url}"\nexport CLOUDFLARE_PROXY_SECRET="{proxy_secret}"\n',
 
139
  ENV_FILE.chmod(0o600)
140
 
141
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  def resolve_account_and_subdomain(api_token: str) -> tuple[str, str]:
143
  account_id = os.environ.get("CLOUDFLARE_ACCOUNT_ID", "").strip()
144
  if not account_id:
 
154
  return account_id, subdomain
155
 
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  def main() -> int:
158
  existing_url = os.environ.get("CLOUDFLARE_PROXY_URL", "").strip()
159
  existing_secret = os.environ.get("CLOUDFLARE_PROXY_SECRET", "").strip()
 
191
  )
192
  write_env(f"https://{worker_name}.{subdomain}.workers.dev", proxy_secret)
193
 
 
194
  return 0
195
  except Exception as exc:
196
  print(f"Cloudflare proxy setup failed: {exc}", file=sys.stderr)
 
197
  return 1
198
 
199
 
setup-uptimerobot.sh DELETED
@@ -1,73 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
-
4
- API_URL="https://api.uptimerobot.com/v2"
5
- API_KEY="${UPTIMEROBOT_API_KEY:-}"
6
- SPACE_HOST_INPUT="${1:-${SPACE_HOST:-}}"
7
- STATUS_FILE="/tmp/huggingmess-uptimerobot-status.json"
8
-
9
- if [ -z "$API_KEY" ]; then
10
- echo "Missing UPTIMEROBOT_API_KEY."
11
- exit 1
12
- fi
13
-
14
- if [ -z "$SPACE_HOST_INPUT" ]; then
15
- echo "Missing Space host."
16
- exit 1
17
- fi
18
-
19
- SPACE_HOST_CLEAN="${SPACE_HOST_INPUT#https://}"
20
- SPACE_HOST_CLEAN="${SPACE_HOST_CLEAN#http://}"
21
- SPACE_HOST_CLEAN="${SPACE_HOST_CLEAN%%/*}"
22
- MONITOR_URL="https://${SPACE_HOST_CLEAN}/health"
23
- MONITOR_NAME="${UPTIMEROBOT_MONITOR_NAME:-HuggingMess ${SPACE_HOST_CLEAN}}"
24
- INTERVAL="${UPTIMEROBOT_INTERVAL:-300}"
25
-
26
- MONITORS_RESPONSE=$(curl -sS -X POST "${API_URL}/getMonitors" \
27
- -d "api_key=${API_KEY}" \
28
- -d "format=json" \
29
- -d "logs=0" \
30
- -d "response_times=0" \
31
- -d "response_times_limit=1")
32
-
33
- MONITOR_ID=$(printf '%s' "$MONITORS_RESPONSE" | jq -r --arg url "$MONITOR_URL" '
34
- (.monitors // []) | map(select(.url == $url)) | first | .id // empty
35
- ')
36
-
37
- if [ -n "$MONITOR_ID" ]; then
38
- printf '{"configured":true,"monitorId":"%s","url":"%s","alreadyExisted":true,"timestamp":"%s"}\n' \
39
- "$MONITOR_ID" "$MONITOR_URL" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$STATUS_FILE"
40
- echo "UptimeRobot monitor already exists for ${MONITOR_URL}"
41
- exit 0
42
- fi
43
-
44
- CURL_ARGS=(
45
- -sS
46
- -X POST "${API_URL}/newMonitor"
47
- -d "api_key=${API_KEY}"
48
- -d "format=json"
49
- -d "type=1"
50
- -d "friendly_name=${MONITOR_NAME}"
51
- -d "url=${MONITOR_URL}"
52
- -d "interval=${INTERVAL}"
53
- )
54
-
55
- if [ -n "${UPTIMEROBOT_ALERT_CONTACTS:-}" ]; then
56
- CURL_ARGS+=(-d "alert_contacts=${UPTIMEROBOT_ALERT_CONTACTS}")
57
- fi
58
-
59
- CREATE_RESPONSE=$(curl "${CURL_ARGS[@]}")
60
- CREATE_STATUS=$(printf '%s' "$CREATE_RESPONSE" | jq -r '.stat // "fail"')
61
-
62
- if [ "$CREATE_STATUS" != "ok" ]; then
63
- printf '{"configured":false,"error":"creation failed","timestamp":"%s"}\n' \
64
- "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$STATUS_FILE"
65
- echo "Failed to create UptimeRobot monitor."
66
- printf '%s\n' "$CREATE_RESPONSE"
67
- exit 1
68
- fi
69
-
70
- NEW_ID=$(printf '%s' "$CREATE_RESPONSE" | jq -r '.monitor.id // empty')
71
- printf '{"configured":true,"monitorId":"%s","url":"%s","timestamp":"%s"}\n' \
72
- "${NEW_ID:-}" "$MONITOR_URL" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$STATUS_FILE"
73
- echo "Created UptimeRobot monitor ${NEW_ID:-"(id unavailable)"} for ${MONITOR_URL}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
start.sh CHANGED
@@ -60,6 +60,11 @@ if [ -n "${CLOUDFLARE_WORKERS_TOKEN:-}" ] || [ -n "${CLOUDFLARE_PROXY_URL:-}" ];
60
  fi
61
  fi
62
 
 
 
 
 
 
63
  if [ -n "${TELEGRAM_USER_IDS:-}" ] && [ -z "${TELEGRAM_ALLOWED_USERS:-}" ]; then
64
  export TELEGRAM_ALLOWED_USERS="$TELEGRAM_USER_IDS"
65
  elif [ -n "${TELEGRAM_USER_ID:-}" ] && [ -z "${TELEGRAM_ALLOWED_USERS:-}" ]; then
@@ -278,11 +283,6 @@ trap graceful_shutdown SIGTERM SIGINT
278
  node "$APP_DIR/health-server.js" &
279
  HEALTH_PID=$!
280
 
281
- if [ "${UPTIMEROBOT_ENABLED:-false}" = "true" ] && [ -n "${UPTIMEROBOT_API_KEY:-}" ] && [ -n "${SPACE_HOST:-}" ]; then
282
- echo "Setting up UptimeRobot monitor..."
283
- bash "$APP_DIR/setup-uptimerobot.sh" "${SPACE_HOST}" || true
284
- fi
285
-
286
  if [ -n "${WEBHOOK_URL:-}" ]; then
287
  python - <<'PY' >/dev/null 2>&1 &
288
  import json, os, urllib.request
 
60
  fi
61
  fi
62
 
63
+ if [ -n "${CLOUDFLARE_WORKERS_TOKEN:-}" ]; then
64
+ echo "Preparing Cloudflare Keepalive worker..."
65
+ python "$APP_DIR/cloudflare-keepalive-setup.py" || true
66
+ fi
67
+
68
  if [ -n "${TELEGRAM_USER_IDS:-}" ] && [ -z "${TELEGRAM_ALLOWED_USERS:-}" ]; then
69
  export TELEGRAM_ALLOWED_USERS="$TELEGRAM_USER_IDS"
70
  elif [ -n "${TELEGRAM_USER_ID:-}" ] && [ -z "${TELEGRAM_ALLOWED_USERS:-}" ]; then
 
283
  node "$APP_DIR/health-server.js" &
284
  HEALTH_PID=$!
285
 
 
 
 
 
 
286
  if [ -n "${WEBHOOK_URL:-}" ]; then
287
  python - <<'PY' >/dev/null 2>&1 &
288
  import json, os, urllib.request