feat(comments): 60s dedupe window + idempotency key
- Server window raised from 5s to 60s so slow double-fires (observed on /videos/CRKAf: same body saved 3-7 times, seconds to minutes apart) collapse into one comment. - X-Idempotency-Key honored: the client sends a per-attempt UUID; the server caches the resulting comment id for 5 minutes under that key. A retry with the same key returns the first-written comment without touching the DB a second time. - Comment and reply submit JS both send the key via fetch(). The existing _busy flag + disabled submit button already covers the fast double-click case on the client; this hardens the server side so duplicates can't slip through even when the client guard is bypassed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
3122420a3d
commit
c9123bc7b8
2 files changed
+51
-7
No files matched your search
@@ -1135,9 +1135,17 @@ async function postComment() {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Posting…';
|
||||
try {
|
||||
// Idempotency key: random per attempt. On retry the client reuses the
|
||||
// same key so the server returns the original comment instead of
|
||||
// writing a second one.
|
||||
const idemKey = (window.crypto?.randomUUID?.() || (Date.now() + '-' + Math.random().toString(36).slice(2)));
|
||||
const r = await fetch('/videos/' + YTC.videoId + '/comments', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': YTC.csrf },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': YTC.csrf,
|
||||
'X-Idempotency-Key': idemKey,
|
||||
},
|
||||
body: JSON.stringify({ body: text })
|
||||
});
|
||||
if (!(r.headers.get('content-type') || '').includes('application/json')) {
|
||||
@@ -1178,9 +1186,14 @@ async function postReply(vid, parentId) {
|
||||
btn.textContent = 'Posting…';
|
||||
}
|
||||
try {
|
||||
const idemKey = (window.crypto?.randomUUID?.() || (Date.now() + '-' + Math.random().toString(36).slice(2)));
|
||||
const r = await fetch('/videos/' + vid + '/comments', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': YTC.csrf },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': YTC.csrf,
|
||||
'X-Idempotency-Key': idemKey,
|
||||
},
|
||||
body: JSON.stringify({ body: text, parent_id: parentId })
|
||||
});
|
||||
if (!(r.headers.get('content-type') || '').includes('application/json')) {
|
||||
|
||||
Reference in new issue
Block a user