Restore
Restore
Restore file to captured state
Every captured change carries its SHA-256-addressed content in BLOB_STORE. Restoring writes that content back to the original path — current file gets backed up to <path>.drift_restore_backup_<epoch> before overwrite.
What will change
Preview -- nothing has been written yet.
| Target file | /var/www/vhosts/3dshawn.com/site1/TEMPLATES/diff.html |
| Site | DriftSense self-monitor on local |
| Kind | local |
| Captured at | 2026-07-10 22:19:14 |
| Captured SHA | 5cfd896e2a84b3428f454afa5e78d63af0dc21cbc497783716e93865dd9f970f |
Current state on disk
Live check just now. If SHAs match, the restore is a no-op.
| File present | yes |
| Size | 6255 bytes |
| Current SHA | 595c27907b0d |
| Same as captured? | no -- restore will change it |
What restore will change — live diff
Left column shows current on-disk content; right shows what restore will write. +0 additions, -70 deletions, 45 unchanged context lines.
| 1 | 1 | <div class="page-head"> |
| 2 | 2 | <span class="page-eyebrow"><span class="dot"></span> Diff</span> |
| 3 | 3 | <h1 class="page-title">$title</h1> |
| 4 | 4 | <p class="page-subtitle">$subtitle</p> |
| 5 | 5 | </div> |
| 6 | 6 | |
| 7 | 7 | <div class="dash-grid" style="grid-template-columns:repeat(4,1fr);margin-bottom:16px"> |
| 8 | 8 | <div class="module glow-emerald"><div class="module-head"><div class="left"><div class="module-title">Added</div></div></div><div class="module-body kpi kpi-emerald"><div class="num">+$ct_add</div><div class="lbl">lines</div></div></div> |
| 9 | 9 | <div class="module glow-rose"><div class="module-head"><div class="left"><div class="module-title">Removed</div></div></div><div class="module-body kpi kpi-rose"><div class="num">-$ct_del</div><div class="lbl">lines</div></div></div> |
| 10 | 10 | <div class="module glow-teal"><div class="module-head"><div class="left"><div class="module-title">Context</div></div></div><div class="module-body kpi kpi-teal"><div class="num">$ct_context</div><div class="lbl">unchanged</div></div></div> |
| 11 | 11 | <div class="module glow-sky"><div class="module-head"><div class="left"><div class="module-title">Blobs</div></div></div><div class="module-body kpi kpi-sky"><div class="num" style="font-size:14px;letter-spacing:0"><span class="dim">from</span> $prev_sha<br><span class="dim">to</span> $curr_sha</div></div></div> |
| 12 | </div> | |
| 13 | ||
| 14 | [if:$restore_url] | |
| 15 | <div style="display:flex;justify-content:flex-end;gap:8px;margin:-4px 0 12px"> | |
| 16 | <button type="button" id="dsCompileBtn" class="tr-chip" | |
| 17 | data-id="$id" data-kind="$kind" | |
| 18 | style="border-color:var(--accent-warn);color:var(--accent-warn-hi)"> | |
| 19 | Test compile <span style="font-size:9.5px;letter-spacing:1px;color:var(--text-muted)">(perl / node / bash / py)</span> | |
| 20 | </button> | |
| 21 | <a href="$restore_url" class="tr-chip" style="border-color:var(--accent);color:var(--accent-hi)">Restore this content →</a> | |
| 22 | 12 | </div> |
| 23 | ||
| 24 | <div id="dsCompileResult" style="display:none;margin:0 0 16px;padding:12px 16px;border-radius:10px;font-family:var(--mono);font-size:12.5px;line-height:1.55"></div> | |
| 25 | ||
| 26 | <script> | |
| 27 | (function () { | |
| 28 | var btn = document.getElementById('dsCompileBtn'); | |
| 29 | var out = document.getElementById('dsCompileResult'); | |
| 30 | if (!btn) return; | |
| 31 | btn.addEventListener('click', async function () { | |
| 32 | if (btn.getAttribute('data-kind') !== 'file') { | |
| 33 | alert('Compile check only supports file changes.'); | |
| 34 | return; | |
| 35 | } | |
| 36 | var origText = btn.innerHTML; | |
| 37 | btn.disabled = true; | |
| 38 | btn.textContent = 'Checking...'; | |
| 39 | out.style.display = 'block'; | |
| 40 | out.style.background = 'var(--bg-2)'; | |
| 41 | out.style.border = '1px solid var(--border)'; | |
| 42 | out.style.color = 'var(--text-dim)'; | |
| 43 | out.textContent = 'Running compile check...'; | |
| 44 | ||
| 45 | try { | |
| 46 | var r = await fetch('/diff.cgi?op=compile&id=' + btn.getAttribute('data-id'), { cache: 'no-store' }); | |
| 47 | var j = await r.json(); | |
| 48 | var color = j.status === 'ok' ? 'rgba(52,211,153,' | |
| 49 | : j.status === 'error' ? 'rgba(244,63,94,' | |
| 50 | : 'rgba(245,158,11,'; | |
| 51 | out.style.background = color + '0.08)'; | |
| 52 | out.style.border = '1px solid ' + color + '0.35)'; | |
| 53 | out.style.color = j.status === 'error' ? '#fda4af' | |
| 54 | : j.status === 'ok' ? '#86efac' | |
| 55 | : '#fcd34d'; | |
| 56 | var head = document.createElement('div'); | |
| 57 | head.style.marginBottom = '6px'; | |
| 58 | head.style.fontWeight = '700'; | |
| 59 | head.style.fontFamily = 'var(--sans)'; | |
| 60 | head.textContent = (j.status === 'ok' ? '✓ Compiles cleanly' | |
| 61 | : j.status === 'error' ? '✗ Syntax errors' | |
| 62 | : '⚠ Unchecked') | |
| 63 | + ' (' + (j.language || '?') + ')'; | |
| 64 | out.innerHTML = ''; | |
| 65 | out.appendChild(head); | |
| 66 | var body = document.createElement('pre'); | |
| 67 | body.style.margin = '0'; | |
| 68 | body.style.whiteSpace = 'pre-wrap'; | |
| 69 | body.style.wordWrap = 'break-word'; | |
| 70 | body.style.fontSize = '12px'; | |
| 71 | body.textContent = j.message || ''; | |
| 72 | out.appendChild(body); | |
| 73 | } catch (e) { | |
| 74 | out.textContent = 'Compile check failed: ' + e.message; | |
| 75 | } finally { | |
| 76 | btn.disabled = false; | |
| 77 | btn.innerHTML = origText; | |
| 78 | } | |
| 79 | }); | |
| 80 | })(); | |
| 81 | </script> | |
| 82 | [/if] | |
| 83 | 13 | |
| 84 | 14 | <div class="module glow-teal"> |
| 85 | 15 | <div class="module-head"><div class="left"><div class="module-title">Unified diff</div><span class="module-subtitle">Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.</span></div></div> |
| 86 | 16 | <div class="module-body tight"> |
| 87 | 17 | [if:$has_diff] |
| 88 | 18 | <style> |
| 89 | 19 | .diff-tbl { width:100%; border-collapse:collapse; font-family:var(--mono); font-size:12.5px; line-height:1.5; } |
| 90 | 20 | .diff-tbl td { padding:1px 8px; vertical-align:top; white-space:pre-wrap; word-wrap:break-word; } |
| 91 | 21 | .diff-tbl .ln { color:var(--text-muted); text-align:right; width:44px; user-select:none; border-right:1px solid var(--border); font-size:11px; } |
| 92 | 22 | .diff-tbl tr.op-add td.text { background:rgba(52,211,153,.10); color:#a7f3d0; } |
| 93 | 23 | .diff-tbl tr.op-add td.ln { background:rgba(52,211,153,.05); color:var(--accent-ok); } |
| 94 | 24 | .diff-tbl tr.op-add td.text::before { content:'+'; color:var(--accent-ok); padding-right:6px; } |
| 95 | 25 | .diff-tbl tr.op-del td.text { background:rgba(244,63,94,.10); color:var(--accent-danger-hi); } |
| 96 | 26 | .diff-tbl tr.op-del td.ln { background:rgba(244,63,94,.05); color:var(--accent-danger); } |
| 97 | 27 | .diff-tbl tr.op-del td.text::before { content:'-'; color:var(--accent-danger); padding-right:6px; } |
| 98 | 28 | .diff-tbl tr.op-ctx td { color:var(--text-dim); } |
| 99 | 29 | .diff-tbl tr.op-ctx td.text::before { content:' '; padding-right:6px; } |
| 100 | 30 | </style> |
| 101 | 31 | <div style="max-height:70vh;overflow:auto"> |
| 102 | 32 | <table class="diff-tbl"> |
| 103 | 33 | <tbody> |
| 104 | 34 | [loop:@lines]<tr class="op-$op"><td class="ln">$old_n</td><td class="ln">$new_n</td><td class="text">$text</td></tr>[/loop] |
| 105 | 35 | </tbody> |
| 106 | 36 | </table> |
| 107 | 37 | </div> |
| 108 | 38 | [/if] |
| 109 | 39 | [if:!$has_diff] |
| 110 | 40 | <div style="padding:36px 14px;text-align:center;color:var(--text-dim);font-size:13.5px"> |
| 111 | 41 | Nothing to diff. Blob may have been auto-purged, or this is the first snapshot. |
| 112 | 42 | </div> |
| 113 | 43 | [/if] |
| 114 | 44 | </div> |
| 115 | 45 | </div> |
Confirm restore
Backs current content to <target>.drift_restore_backup_<epoch>, writes the captured content, verifies SHA post-write.