somratpro commited on
Commit
c53bf2f
·
1 Parent(s): ef67ca7

refactor: enhance sync status handling for dashboard compatibility

Browse files
Files changed (2) hide show
  1. health-server.js +55 -15
  2. paperclip-sync.py +8 -0
health-server.js CHANGED
@@ -9,7 +9,8 @@ const APP_HOST = "127.0.0.1";
9
  const startTime = Date.now();
10
  const INVITE_URL_FILE = "/tmp/invite-url.txt";
11
  const SYNC_STATUS_FILE = "/tmp/sync-status.json";
12
- const CLOUDFLARE_KEEPALIVE_STATUS_FILE = "/tmp/huggingclip-cloudflare-keepalive-status.json";
 
13
 
14
  function parseRequestUrl(url) {
15
  try {
@@ -22,7 +23,15 @@ function parseRequestUrl(url) {
22
  function getSyncStatus() {
23
  try {
24
  if (fs.existsSync(SYNC_STATUS_FILE)) {
25
- return JSON.parse(fs.readFileSync(SYNC_STATUS_FILE, "utf8"));
 
 
 
 
 
 
 
 
26
  }
27
  } catch {}
28
  if (process.env.HF_TOKEN) {
@@ -37,7 +46,9 @@ function getSyncStatus() {
37
  function getKeepaliveStatus() {
38
  try {
39
  if (fs.existsSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE)) {
40
- return JSON.parse(fs.readFileSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE, "utf8"));
 
 
41
  }
42
  } catch {}
43
  return null;
@@ -96,7 +107,13 @@ function toneBadge(label, tone = "neutral") {
96
  return `<span class="badge ${tone}">${escapeHtml(label)}</span>`;
97
  }
98
 
99
- function renderTile({ title, value, detail = "", tone = "neutral", meta = "" }) {
 
 
 
 
 
 
100
  return `<article class="tile ${tone}">
101
  <div class="tile-head">
102
  <span class="tile-title">${escapeHtml(title)}</span>
@@ -110,12 +127,16 @@ function renderTile({ title, value, detail = "", tone = "neutral", meta = "" })
110
 
111
  function renderDashboard(data) {
112
  const syncStatus = String(data.sync?.status || "unknown");
113
- const syncTone = ["success", "restored", "synced", "configured"].includes(syncStatus)
 
 
114
  ? "ok"
115
  : syncStatus === "disabled"
116
  ? "warn"
117
  : "neutral";
118
- const backupDetail = data.sync?.message ? escapeHtml(data.sync.message) : "No status yet";
 
 
119
 
120
  const keepaliveConfigured = data.keepalive?.configured === true;
121
  const keepaliveStatus = String(
@@ -138,7 +159,10 @@ function renderDashboard(data) {
138
  const tiles = [
139
  renderTile({
140
  title: "Paperclip Core",
141
- value: toneBadge(data.appReady ? "Online" : "Booting", data.appReady ? "ok" : "warn"),
 
 
 
142
  detail: `Backend Port ${APP_PORT}`,
143
  tone: data.appReady ? "ok" : "warn",
144
  }),
@@ -162,7 +186,10 @@ function renderDashboard(data) {
162
  }),
163
  renderTile({
164
  title: "Keep Awake",
165
- value: toneBadge(keepaliveConfigured ? "CF Cron" : keepaliveStatus.toUpperCase(), keepAliveTone),
 
 
 
166
  detail: keepAliveDetail,
167
  tone: keepAliveTone,
168
  }),
@@ -175,14 +202,14 @@ function renderDashboard(data) {
175
  <meta name="viewport" content="width=device-width, initial-scale=1" />
176
  <title>HuggingClip</title>
177
  <style>
178
- :root { color-scheme: dark; --bg:#08080f; --panel:#12111b; --panel2:#151421; --line:#26243a; --text:#f6f4ff; --muted:#7f7a9e; --soft:#b8b3d7; --good:#22c55e; --warn:#f5c542; --bad:#fb7185; --accent:#3b82f6; --accent2:#8b5cf6; }
179
  * { box-sizing:border-box; }
180
  body { margin:0; min-height:100vh; font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background:var(--bg); color:var(--text); font-size:13px; }
181
  main { width:min(720px, calc(100% - 32px)); margin:0 auto; padding:36px 0 44px; }
182
  header { text-align:center; margin-bottom:22px; }
183
  h1 { margin:0; font-size:1.65rem; line-height:1; letter-spacing:0; }
184
  .subtitle { margin-top:12px; color:var(--muted); font-size:.72rem; text-transform:uppercase; letter-spacing:.14em; font-weight:800; }
185
- .hero-action { display:flex; width:100%; min-height:46px; align-items:center; justify-content:center; border-radius:8px; background:linear-gradient(135deg, var(--accent), var(--accent2)); color:#ffffff; text-decoration:none; font-weight:850; font-size:.98rem; margin:24px 0 20px; transition: opacity 0.15s ease; }
186
  .hero-action:hover { opacity: 0.9; }
187
  .invite-banner { background:rgba(245,197,66,.1); border:1px solid rgba(245,197,66,.2); border-radius:8px; padding:12px 16px; margin-bottom:20px; display:flex; flex-direction:column; gap:6px; }
188
  .invite-banner span { color:var(--warn); font-weight:850; font-size:.75rem; text-transform:uppercase; }
@@ -219,11 +246,15 @@ function renderDashboard(data) {
219
  <h1>🧬 HuggingClip</h1>
220
  <div class="subtitle">Paperclip Orchestrator Dashboard</div>
221
  </header>
222
- ${inviteUrl ? `
 
 
223
  <div class="invite-banner">
224
  <span>Admin Setup Required</span>
225
  <code>${escapeHtml(inviteUrl)}</code>
226
- </div>` : ""}
 
 
227
  <a class="hero-action" href="/app/" target="_blank" rel="noopener noreferrer">Open Paperclip UI -></a>
228
  <section class="overview">
229
  ${tiles}
@@ -293,7 +324,12 @@ const server = http.createServer(async (req, res) => {
293
  proxyReq.on("error", () => {
294
  if (!res.headersSent) {
295
  res.writeHead(503, { "Content-Type": "application/json" });
296
- res.end(JSON.stringify({ status: "starting", message: "Paperclip is booting..." }));
 
 
 
 
 
297
  } else {
298
  res.end();
299
  }
@@ -306,7 +342,9 @@ server.on("upgrade", (req, socket, head) => {
306
  const url = parseRequestUrl(req.url);
307
  const proxyPath = url.pathname;
308
  const proxySocket = net.connect(APP_PORT, APP_HOST, () => {
309
- proxySocket.write(`${req.method} ${proxyPath}${url.search} HTTP/${req.httpVersion}\r\n`);
 
 
310
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
311
  proxySocket.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`);
312
  }
@@ -320,5 +358,7 @@ server.on("upgrade", (req, socket, head) => {
320
  server.timeout = 0;
321
  server.keepAliveTimeout = 65000;
322
  server.listen(PORT, "0.0.0.0", () =>
323
- console.log(`🧬 HuggingClip Dashboard on ${PORT} -> Paperclip on ${APP_PORT}`),
 
 
324
  );
 
9
  const startTime = Date.now();
10
  const INVITE_URL_FILE = "/tmp/invite-url.txt";
11
  const SYNC_STATUS_FILE = "/tmp/sync-status.json";
12
+ const CLOUDFLARE_KEEPALIVE_STATUS_FILE =
13
+ "/tmp/huggingclip-cloudflare-keepalive-status.json";
14
 
15
  function parseRequestUrl(url) {
16
  try {
 
23
  function getSyncStatus() {
24
  try {
25
  if (fs.existsSync(SYNC_STATUS_FILE)) {
26
+ const raw = fs.readFileSync(SYNC_STATUS_FILE, "utf8");
27
+ const parsed = JSON.parse(raw);
28
+ if (!parsed.status && parsed.db_status) parsed.status = parsed.db_status;
29
+ if (!parsed.message) {
30
+ if (parsed.last_error) parsed.message = parsed.last_error;
31
+ else if (parsed.last_sync_time)
32
+ parsed.message = `Last sync: ${parsed.last_sync_time}`;
33
+ }
34
+ return parsed;
35
  }
36
  } catch {}
37
  if (process.env.HF_TOKEN) {
 
46
  function getKeepaliveStatus() {
47
  try {
48
  if (fs.existsSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE)) {
49
+ return JSON.parse(
50
+ fs.readFileSync(CLOUDFLARE_KEEPALIVE_STATUS_FILE, "utf8"),
51
+ );
52
  }
53
  } catch {}
54
  return null;
 
107
  return `<span class="badge ${tone}">${escapeHtml(label)}</span>`;
108
  }
109
 
110
+ function renderTile({
111
+ title,
112
+ value,
113
+ detail = "",
114
+ tone = "neutral",
115
+ meta = "",
116
+ }) {
117
  return `<article class="tile ${tone}">
118
  <div class="tile-head">
119
  <span class="tile-title">${escapeHtml(title)}</span>
 
127
 
128
  function renderDashboard(data) {
129
  const syncStatus = String(data.sync?.status || "unknown");
130
+ const syncTone = ["success", "restored", "synced", "configured"].includes(
131
+ syncStatus,
132
+ )
133
  ? "ok"
134
  : syncStatus === "disabled"
135
  ? "warn"
136
  : "neutral";
137
+ const backupDetail = data.sync?.message
138
+ ? escapeHtml(data.sync.message)
139
+ : "No status yet";
140
 
141
  const keepaliveConfigured = data.keepalive?.configured === true;
142
  const keepaliveStatus = String(
 
159
  const tiles = [
160
  renderTile({
161
  title: "Paperclip Core",
162
+ value: toneBadge(
163
+ data.appReady ? "Online" : "Booting",
164
+ data.appReady ? "ok" : "warn",
165
+ ),
166
  detail: `Backend Port ${APP_PORT}`,
167
  tone: data.appReady ? "ok" : "warn",
168
  }),
 
186
  }),
187
  renderTile({
188
  title: "Keep Awake",
189
+ value: toneBadge(
190
+ keepaliveConfigured ? "CF Cron" : keepaliveStatus.toUpperCase(),
191
+ keepAliveTone,
192
+ ),
193
  detail: keepAliveDetail,
194
  tone: keepAliveTone,
195
  }),
 
202
  <meta name="viewport" content="width=device-width, initial-scale=1" />
203
  <title>HuggingClip</title>
204
  <style>
205
+ :root { color-scheme: dark; --bg:#08080f; --panel:#12111b; --panel2:#151421; --line:#26243a; --text:#f6f4ff; --muted:#7f7a9e; --soft:#b8b3d7; --good:#22c55e; --warn:#f5c542; --bad:#fb7185;}
206
  * { box-sizing:border-box; }
207
  body { margin:0; min-height:100vh; font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background:var(--bg); color:var(--text); font-size:13px; }
208
  main { width:min(720px, calc(100% - 32px)); margin:0 auto; padding:36px 0 44px; }
209
  header { text-align:center; margin-bottom:22px; }
210
  h1 { margin:0; font-size:1.65rem; line-height:1; letter-spacing:0; }
211
  .subtitle { margin-top:12px; color:var(--muted); font-size:.72rem; text-transform:uppercase; letter-spacing:.14em; font-weight:800; }
212
+ .hero-action { display:flex; width:100%; min-height:46px; align-items:center; justify-content:center; border-radius:8px; background:#fff; color:#000; text-decoration:none; font-weight:850; font-size:.98rem; margin:24px 0 20px; transition: opacity 0.15s ease; }
213
  .hero-action:hover { opacity: 0.9; }
214
  .invite-banner { background:rgba(245,197,66,.1); border:1px solid rgba(245,197,66,.2); border-radius:8px; padding:12px 16px; margin-bottom:20px; display:flex; flex-direction:column; gap:6px; }
215
  .invite-banner span { color:var(--warn); font-weight:850; font-size:.75rem; text-transform:uppercase; }
 
246
  <h1>🧬 HuggingClip</h1>
247
  <div class="subtitle">Paperclip Orchestrator Dashboard</div>
248
  </header>
249
+ ${
250
+ inviteUrl
251
+ ? `
252
  <div class="invite-banner">
253
  <span>Admin Setup Required</span>
254
  <code>${escapeHtml(inviteUrl)}</code>
255
+ </div>`
256
+ : ""
257
+ }
258
  <a class="hero-action" href="/app/" target="_blank" rel="noopener noreferrer">Open Paperclip UI -></a>
259
  <section class="overview">
260
  ${tiles}
 
324
  proxyReq.on("error", () => {
325
  if (!res.headersSent) {
326
  res.writeHead(503, { "Content-Type": "application/json" });
327
+ res.end(
328
+ JSON.stringify({
329
+ status: "starting",
330
+ message: "Paperclip is booting...",
331
+ }),
332
+ );
333
  } else {
334
  res.end();
335
  }
 
342
  const url = parseRequestUrl(req.url);
343
  const proxyPath = url.pathname;
344
  const proxySocket = net.connect(APP_PORT, APP_HOST, () => {
345
+ proxySocket.write(
346
+ `${req.method} ${proxyPath}${url.search} HTTP/${req.httpVersion}\r\n`,
347
+ );
348
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
349
  proxySocket.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`);
350
  }
 
358
  server.timeout = 0;
359
  server.keepAliveTimeout = 65000;
360
  server.listen(PORT, "0.0.0.0", () =>
361
+ console.log(
362
+ `🧬 HuggingClip Dashboard on ${PORT} -> Paperclip on ${APP_PORT}`,
363
+ ),
364
  );
paperclip-sync.py CHANGED
@@ -101,6 +101,14 @@ def parse_db_url(db_url: str) -> dict:
101
  def write_status(status: dict):
102
  """Write sync status to file for dashboard"""
103
  try:
 
 
 
 
 
 
 
 
104
  STATUS_FILE.write_text(json.dumps(status, indent=2))
105
  except Exception as e:
106
  logger.error(f'Failed to write status file: {e}')
 
101
  def write_status(status: dict):
102
  """Write sync status to file for dashboard"""
103
  try:
104
+ # Ensure compatibility with dashboard fields: `status` and `message`
105
+ try:
106
+ if 'db_status' in status and 'status' not in status:
107
+ status['status'] = status['db_status']
108
+ if 'last_error' in status and 'message' not in status:
109
+ status['message'] = status['last_error']
110
+ except Exception:
111
+ pass
112
  STATUS_FILE.write_text(json.dumps(status, indent=2))
113
  except Exception as e:
114
  logger.error(f'Failed to write status file: {e}')