Skip to content

Commit 8fea819

Browse files
committed
edt
1 parent a09bfbb commit 8fea819

File tree

3 files changed

+182
-164
lines changed

3 files changed

+182
-164
lines changed

html/error-template.html

Lines changed: 147 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
.error-message { font-size: 24px; margin: 20px 0; }
1212
.gif { width: 700px; margin-bottom: 25px; }
1313
.footer { color: #aaa; font-size: 14px; margin-top: 40px; }
14+
.report-btn { background: #ef4444; color: white; border: none; border-radius: 8px; padding: 10px 20px; font-size: 16px; cursor: pointer; margin-top: 20px; }
15+
.report-btn:hover { background: #dc2626; }
1416
</style>
1517
</head>
1618
<body>
@@ -19,156 +21,172 @@
1921
<div class="error-type">ERROR_TYPE</div>
2022
<div id="error-message" class="error-message">ERROR_MESSAGE</div>
2123
<img class="gif" src="ERROR_GIF" alt="Error illustration">
22-
<button id="open-report-modal" data-site="MySite" data-link="https://example.com">Signaler une erreur</button>
24+
<!-- New report button -->
25+
<button id="open-report-modal" class="report-btn" data-link="" data-site="">Signaler cette erreur</button>
2326
</div>
27+
<!-- Embedded front-end script for modal and reporting -->
2428
<script>
2529
// Front script - creates modal, grabs data, calls the worker, shows banner
2630
// ====== helpers ======
31+
// Try to extract an error code from the page
2732
const getErrorCodeFromPage = () => {
28-
const idEl = document.querySelector('#error-code');
29-
if (idEl && idEl.textContent.trim()) return idEl.textContent.trim();
30-
const bodyAttr = document.body?.getAttribute('data-error-code');
31-
if (bodyAttr) return bodyAttr.trim();
32-
const meta = document.querySelector('meta[name="error-code"]');
33-
if (meta && meta.getAttribute('content')) return meta.getAttribute('content').trim();
34-
const p = new URLSearchParams(location.search).get('errorCode');
35-
if (p) return p.trim();
36-
return 'N/A';
37-
};
33+
// #error-code element text
34+
const idEl = document.querySelector('#error-code')
35+
if (idEl && idEl.textContent.trim()) return idEl.textContent.trim()
36+
// body[data-error-code]
37+
const bodyAttr = document.body?.getAttribute('data-error-code')
38+
if (bodyAttr) return bodyAttr.trim()
39+
// <meta name="error-code" content="...">
40+
const meta = document.querySelector('meta[name="error-code"]')
41+
if (meta && meta.getAttribute('content')) return meta.getAttribute('content').trim()
42+
// ?errorCode=XYZ
43+
const p = new URLSearchParams(location.search).get('errorCode')
44+
if (p) return p.trim()
45+
// fallback
46+
return 'N/A'
47+
}
48+
// Create a top-of-page success alert
3849
const showTopAlert = msg => {
39-
const bar = document.createElement('div');
40-
bar.textContent = msg;
41-
bar.setAttribute('role', 'status');
42-
bar.style.position = 'fixed';
43-
bar.style.top = '0';
44-
bar.style.left = '0';
45-
bar.style.right = '0';
46-
bar.style.padding = '12px 16px';
47-
bar.style.textAlign = 'center';
48-
bar.style.fontWeight = '600';
49-
bar.style.background = '#16a34a';
50-
bar.style.color = 'white';
51-
bar.style.zIndex = '2147483647';
52-
document.body.appendChild(bar);
53-
setTimeout(() => bar.remove(), 4000);
54-
};
50+
const bar = document.createElement('div')
51+
bar.textContent = msg
52+
bar.setAttribute('role', 'status')
53+
bar.style.position = 'fixed'
54+
bar.style.top = '0'
55+
bar.style.left = '0'
56+
bar.style.right = '0'
57+
bar.style.padding = '12px 16px'
58+
bar.style.textAlign = 'center'
59+
bar.style.fontWeight = '600'
60+
bar.style.background = '#16a34a'
61+
bar.style.color = 'white'
62+
bar.style.zIndex = '2147483647'
63+
document.body.appendChild(bar)
64+
setTimeout(() => bar.remove(), 4000)
65+
}
66+
// ====== modal ======
5567
const ensureModal = () => {
56-
if (document.getElementById('report-error-modal')) return;
57-
const overlay = document.createElement('div');
58-
overlay.id = 'report-error-modal';
59-
overlay.style.position = 'fixed';
60-
overlay.style.inset = '0';
61-
overlay.style.background = 'rgba(0,0,0,0.4)';
62-
overlay.style.display = 'none';
63-
overlay.style.alignItems = 'center';
64-
overlay.style.justifyContent = 'center';
65-
overlay.style.zIndex = '2147483646';
66-
const box = document.createElement('div');
67-
box.style.background = 'white';
68-
box.style.borderRadius = '12px';
69-
box.style.padding = '20px';
70-
box.style.width = 'min(92vw, 420px)';
71-
box.style.boxShadow = '0 10px 30px rgba(0,0,0,0.25)';
72-
box.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif';
73-
const title = document.createElement('h2');
74-
title.textContent = '🆘 Erreur signalée';
75-
title.style.margin = '0 0 12px 0';
76-
title.style.fontSize = '20px';
77-
const label = document.createElement('label');
78-
label.textContent = 'Nom / Prénom';
79-
label.style.display = 'block';
80-
label.style.fontSize = '14px';
81-
label.style.marginBottom = '6px';
82-
const input = document.createElement('input');
83-
input.type = 'text';
84-
input.placeholder = 'Ex: Jane Doe';
85-
input.required = true;
86-
input.style.width = '100%';
87-
input.style.boxSizing = 'border-box';
88-
input.style.padding = '10px 12px';
89-
input.style.fontSize = '16px';
90-
input.style.border = '1px solid #d1d5db';
91-
input.style.borderRadius = '8px';
92-
input.id = 'reporter-fullname';
93-
const actions = document.createElement('div');
94-
actions.style.display = 'flex';
95-
actions.style.gap = '8px';
96-
actions.style.marginTop = '16px';
97-
const cancel = document.createElement('button');
98-
cancel.type = 'button';
99-
cancel.textContent = 'Annuler';
100-
cancel.style.flex = '1';
101-
cancel.style.padding = '10px 12px';
102-
cancel.style.border = '1px solid #d1d5db';
103-
cancel.style.borderRadius = '8px';
104-
cancel.style.background = 'white';
105-
cancel.onclick = () => hideModal();
106-
const submit = document.createElement('button');
107-
submit.type = 'button';
108-
submit.textContent = 'Signaler';
109-
submit.style.flex = '1';
110-
submit.style.padding = '10px 12px';
111-
submit.style.border = '0';
112-
submit.style.borderRadius = '8px';
113-
submit.style.background = '#ef4444';
114-
submit.style.color = 'white';
115-
submit.style.fontWeight = '600';
68+
if (document.getElementById('report-error-modal')) return
69+
const overlay = document.createElement('div')
70+
overlay.id = 'report-error-modal'
71+
overlay.style.position = 'fixed'
72+
overlay.style.inset = '0'
73+
overlay.style.background = 'rgba(0,0,0,0.4)'
74+
overlay.style.display = 'none'
75+
overlay.style.alignItems = 'center'
76+
overlay.style.justifyContent = 'center'
77+
overlay.style.zIndex = '2147483646'
78+
const box = document.createElement('div')
79+
box.style.background = 'white'
80+
box.style.borderRadius = '12px'
81+
box.style.padding = '20px'
82+
box.style.width = 'min(92vw, 420px)'
83+
box.style.boxShadow = '0 10px 30px rgba(0,0,0,0.25)'
84+
box.style.fontFamily = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif'
85+
const title = document.createElement('h2')
86+
title.textContent = '🆘 Erreur signalée'
87+
title.style.margin = '0 0 12px 0'
88+
title.style.fontSize = '20px'
89+
const label = document.createElement('label')
90+
label.textContent = 'Nom / Prénom'
91+
label.style.display = 'block'
92+
label.style.fontSize = '14px'
93+
label.style.marginBottom = '6px'
94+
const input = document.createElement('input')
95+
input.type = 'text'
96+
input.placeholder = 'Ex: Jane Doe'
97+
input.required = true
98+
input.style.width = '100%'
99+
input.style.boxSizing = 'border-box'
100+
input.style.padding = '10px 12px'
101+
input.style.fontSize = '16px'
102+
input.style.border = '1px solid #d1d5db'
103+
input.style.borderRadius = '8px'
104+
input.id = 'reporter-fullname'
105+
const actions = document.createElement('div')
106+
actions.style.display = 'flex'
107+
actions.style.gap = '8px'
108+
actions.style.marginTop = '16px'
109+
const cancel = document.createElement('button')
110+
cancel.type = 'button'
111+
cancel.textContent = 'Annuler'
112+
cancel.style.flex = '1'
113+
cancel.style.padding = '10px 12px'
114+
cancel.style.border = '1px solid #d1d5db'
115+
cancel.style.borderRadius = '8px'
116+
cancel.style.background = 'white'
117+
cancel.onclick = () => hideModal()
118+
const submit = document.createElement('button')
119+
submit.type = 'button'
120+
submit.textContent = 'Signaler'
121+
submit.style.flex = '1'
122+
submit.style.padding = '10px 12px'
123+
submit.style.border = '0'
124+
submit.style.borderRadius = '8px'
125+
submit.style.background = '#ef4444'
126+
submit.style.color = 'white'
127+
submit.style.fontWeight = '600'
116128
submit.onclick = async () => {
117-
const fullName = input.value.trim();
129+
const fullName = input.value.trim()
118130
if (!fullName) {
119-
input.focus();
120-
return;
131+
input.focus()
132+
return
121133
}
122-
const trigger = document.getElementById('open-report-modal');
123-
const redirectUrl = trigger?.getAttribute('data-link') || location.href;
124-
const siteName = trigger?.getAttribute('data-site') || location.hostname;
125-
const errorCode = getErrorCodeFromPage();
134+
// Collect data
135+
const trigger = document.getElementById('open-report-modal')
136+
const redirectUrl = trigger?.getAttribute('data-link') || location.href
137+
const siteName = trigger?.getAttribute('data-site') || location.hostname
138+
const errorCode = getErrorCodeFromPage()
139+
// Call the worker endpoint
126140
try {
127141
const res = await fetch('/report-error', {
128142
method: 'POST',
129143
headers: { 'content-type': 'application/json' },
130144
body: JSON.stringify({ fullName, errorCode, siteName, redirectUrl })
131-
});
132-
const data = await res.json().catch(() => ({}));
133-
if (!res.ok || !data.ok) throw new Error(data.error || 'send failed');
134-
hideModal();
135-
showTopAlert('Erreur signalée !');
145+
})
146+
const data = await res.json().catch(() => ({}))
147+
if (!res.ok || !data.ok) throw new Error(data.error || 'send failed')
148+
hideModal()
149+
showTopAlert('Erreur signalée !')
136150
} catch (e) {
137-
hideModal();
138-
showTopAlert('Envoi impossible');
139-
console.error('Discord webhook failed', e);
151+
hideModal()
152+
showTopAlert('Envoi impossible')
153+
console.error('Discord webhook failed', e)
140154
}
141-
};
142-
actions.appendChild(cancel);
143-
actions.appendChild(submit);
144-
box.appendChild(title);
145-
box.appendChild(label);
146-
box.appendChild(input);
147-
box.appendChild(actions);
148-
overlay.appendChild(box);
149-
document.body.appendChild(overlay);
150-
};
155+
}
156+
actions.appendChild(cancel)
157+
actions.appendChild(submit)
158+
box.appendChild(title)
159+
box.appendChild(label)
160+
box.appendChild(input)
161+
box.appendChild(actions)
162+
overlay.appendChild(box)
163+
document.body.appendChild(overlay)
164+
}
151165
const showModal = () => {
152-
ensureModal();
153-
const overlay = document.getElementById('report-error-modal');
154-
const input = document.getElementById('reporter-fullname');
155-
overlay.style.display = 'flex';
156-
setTimeout(() => input?.focus(), 0);
157-
};
166+
ensureModal()
167+
const overlay = document.getElementById('report-error-modal')
168+
const input = document.getElementById('reporter-fullname')
169+
overlay.style.display = 'flex'
170+
setTimeout(() => input?.focus(), 0)
171+
}
158172
const hideModal = () => {
159-
const overlay = document.getElementById('report-error-modal');
160-
if (overlay) overlay.style.display = 'none';
161-
};
173+
const overlay = document.getElementById('report-error-modal')
174+
if (overlay) overlay.style.display = 'none'
175+
}
176+
// ====== wire the button ======
162177
const wireButton = () => {
163-
ensureModal();
164-
const btn = document.getElementById('open-report-modal');
165-
if (!btn) return;
178+
ensureModal()
179+
const btn = document.getElementById('open-report-modal')
180+
if (!btn) return
166181
btn.addEventListener('click', e => {
167-
e.preventDefault();
168-
showModal();
169-
});
170-
};
171-
document.addEventListener('DOMContentLoaded', wireButton);
182+
e.preventDefault()
183+
showModal()
184+
})
185+
// Set dynamic attributes
186+
btn.setAttribute('data-link', location.href)
187+
btn.setAttribute('data-site', location.hostname)
188+
}
189+
document.addEventListener('DOMContentLoaded', wireButton)
172190
</script>
173191
</body>
174192
</html>

worker.js

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,41 @@ async function injectBanner(response, bannerMessage) {
8181

8282
export default {
8383
async fetch(request, env, ctx) {
84-
const url = new URL(request.url);
8584
const host = request.headers.get('host');
85+
const url = new URL(request.url);
86+
87+
// New: Handle /report-error POST for Discord webhook
88+
if (url.pathname === '/report-error' && request.method === 'POST') {
89+
try {
90+
const { fullName, errorCode, siteName, redirectUrl } = await request.json();
91+
if (!fullName || !errorCode || !siteName || !redirectUrl) {
92+
return new Response(JSON.stringify({ ok: false, error: 'Missing required fields' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
93+
}
94+
const embed = {
95+
title: '🆘 Erreur Signalée',
96+
color: 0xef4444, // Red color
97+
fields: [
98+
{ name: 'Nom / Prénom', value: fullName, inline: true },
99+
{ name: 'Code d\'Erreur', value: errorCode, inline: true },
100+
{ name: 'Site', value: siteName, inline: true },
101+
{ name: 'URL', value: redirectUrl, inline: false }
102+
],
103+
timestamp: new Date().toISOString()
104+
};
105+
const webhookResponse = await fetch(env.DISCORD_WEBHOOK_URL, {
106+
method: 'POST',
107+
headers: { 'Content-Type': 'application/json' },
108+
body: JSON.stringify({ embeds: [embed] })
109+
});
110+
if (!webhookResponse.ok) {
111+
throw new Error(`Webhook failed: ${webhookResponse.status}`);
112+
}
113+
return new Response(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });
114+
} catch (error) {
115+
console.error('Report error handling failed:', error);
116+
return new Response(JSON.stringify({ ok: false, error: 'Internal error' }), { status: 500, headers: { 'Content-Type': 'application/json' } });
117+
}
118+
}
86119

87120
// Read state (cache by default)
88121
let state = await getMaintenanceState(env, host);
@@ -133,40 +166,6 @@ export default {
133166
return await injectBanner(response, bannerMessage);
134167
}
135168

136-
// Report error endpoint
137-
if (url.pathname === '/report-error' && request.method === 'POST') {
138-
try {
139-
const { fullName, errorCode, siteName, redirectUrl } = await request.json();
140-
const webhookUrl = env.DISCORD_WEBHOOK_URL;
141-
142-
if (!webhookUrl) {
143-
console.error('Webhook URL not configured');
144-
return new Response(JSON.stringify({ ok: false, error: 'Webhook URL not configured' }), { status: 500 });
145-
}
146-
147-
const payload = {
148-
content: `🚨 **Erreur signalée**\n- **Nom**: ${fullName}\n- **Code d'erreur**: ${errorCode}\n- **Site**: ${siteName}\n- **URL**: ${redirectUrl}`
149-
};
150-
151-
const res = await fetch(webhookUrl, {
152-
method: 'POST',
153-
headers: { 'Content-Type': 'application/json' },
154-
body: JSON.stringify(payload)
155-
});
156-
157-
if (!res.ok) {
158-
const errorText = await res.text();
159-
console.error('Discord webhook failed:', res.status, errorText);
160-
return new Response(JSON.stringify({ ok: false, error: 'Failed to send webhook', details: errorText }), { status: 502 });
161-
}
162-
163-
return new Response(JSON.stringify({ ok: true }), { status: 200 });
164-
} catch (err) {
165-
console.error('Error handling /report-error:', err);
166-
return new Response(JSON.stringify({ ok: false, error: 'Internal Server Error' }), { status: 500 });
167-
}
168-
}
169-
170169
return response;
171170
}
172171
}

0 commit comments

Comments
 (0)