unsubscribe.html
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>Unsubscribe - 333 Method</title> 7 <style> 8 * { 9 margin: 0; 10 padding: 0; 11 box-sizing: border-box; 12 } 13 14 body { 15 font-family: 16 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; 17 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 18 min-height: 100vh; 19 display: flex; 20 align-items: center; 21 justify-content: center; 22 padding: 20px; 23 line-height: 1.6; 24 color: #333; 25 } 26 27 .container { 28 background: white; 29 border-radius: 12px; 30 box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1); 31 max-width: 500px; 32 width: 100%; 33 padding: 40px; 34 text-align: center; 35 } 36 37 h1 { 38 font-size: 28px; 39 margin-bottom: 20px; 40 color: #2d3748; 41 } 42 43 .status-icon { 44 font-size: 64px; 45 margin-bottom: 20px; 46 } 47 48 .loading { 49 color: #667eea; 50 } 51 52 .success { 53 color: #48bb78; 54 } 55 56 .error { 57 color: #f56565; 58 } 59 60 p { 61 font-size: 16px; 62 color: #4a5568; 63 margin-bottom: 15px; 64 } 65 66 .small-text { 67 font-size: 14px; 68 color: #718096; 69 margin-top: 20px; 70 } 71 72 .spinner { 73 border: 4px solid #f3f4f6; 74 border-top: 4px solid #667eea; 75 border-radius: 50%; 76 width: 48px; 77 height: 48px; 78 animation: spin 1s linear infinite; 79 margin: 20px auto; 80 } 81 82 @keyframes spin { 83 0% { 84 transform: rotate(0deg); 85 } 86 100% { 87 transform: rotate(360deg); 88 } 89 } 90 91 .button { 92 display: inline-block; 93 background: #667eea; 94 color: white; 95 padding: 12px 24px; 96 border-radius: 6px; 97 text-decoration: none; 98 font-weight: 500; 99 margin-top: 20px; 100 transition: background 0.2s; 101 } 102 103 .button:hover { 104 background: #5a67d8; 105 } 106 </style> 107 </head> 108 <body> 109 <div class="container"> 110 <div id="loading-state"> 111 <div class="spinner"></div> 112 <h1>Processing your request...</h1> 113 <p>Please wait while we unsubscribe you from our emails.</p> 114 </div> 115 116 <div id="success-state" style="display: none"> 117 <div class="status-icon success">✓</div> 118 <h1>Successfully Unsubscribed</h1> 119 <p>You've been removed from our email list and won't receive any further emails from us.</p> 120 <p class="small-text"> 121 If you received this email in error or change your mind, please contact us directly. 122 </p> 123 </div> 124 125 <div id="error-state" style="display: none"> 126 <div class="status-icon error">✕</div> 127 <h1>Something Went Wrong</h1> 128 <p id="error-message"> 129 We couldn't process your unsubscribe request. The link may be invalid or expired. 130 </p> 131 <p class="small-text"> 132 If you continue to receive emails, please contact us directly for assistance. 133 </p> 134 </div> 135 </div> 136 137 <script> 138 // Get URL parameters 139 const params = new URLSearchParams(window.location.search); 140 const id = params.get('id'); 141 const token = params.get('token'); 142 143 // Cloudflare Worker endpoint (update with your actual Worker URL) 144 const WORKER_URL = 'https://unsubscribe.auditandfix.workers.dev'; 145 146 // Show appropriate state 147 function showState(state, errorMsg = null) { 148 document.getElementById('loading-state').style.display = 'none'; 149 document.getElementById('success-state').style.display = 'none'; 150 document.getElementById('error-state').style.display = 'none'; 151 152 if (state === 'success') { 153 document.getElementById('success-state').style.display = 'block'; 154 } else if (state === 'error') { 155 if (errorMsg) { 156 document.getElementById('error-message').textContent = errorMsg; 157 } 158 document.getElementById('error-state').style.display = 'block'; 159 } 160 } 161 162 // Validate parameters 163 if (!id || !token) { 164 showState( 165 'error', 166 'Invalid unsubscribe link. The link appears to be incomplete or malformed.' 167 ); 168 } else { 169 // Send unsubscribe request 170 fetch(WORKER_URL, { 171 method: 'POST', 172 headers: { 173 'Content-Type': 'application/json', 174 }, 175 body: JSON.stringify({ id, token }), 176 }) 177 .then(response => response.json()) 178 .then(data => { 179 if (data.success) { 180 showState('success'); 181 } else { 182 showState('error', data.error || 'Unable to process unsubscribe request.'); 183 } 184 }) 185 .catch(error => { 186 console.error('Error:', error); 187 showState('error', 'Network error. Please check your connection and try again.'); 188 }); 189 } 190 </script> 191 </body> 192 </html>