Skip to content

Commit a9400ee

Browse files
authored
Merge pull request #1807 from codidact/0valt/1805/sign-in-redirect-fix
Fix redirects to paths only used in AJAX requests after signing in
2 parents 35a302a + 344bac9 commit a9400ee

File tree

7 files changed

+78
-65
lines changed

7 files changed

+78
-65
lines changed

app/assets/javascripts/comments.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,7 @@ $(() => {
188188
const threadId = $tgt.data('thread');
189189
const $modal = $($tgt.data('modal'));
190190

191-
const resp = await fetch(`/comments/thread/${threadId}/followers`, {
192-
method: 'GET',
193-
credentials: 'include',
191+
const resp = await QPixel.fetch(`/comments/thread/${threadId}/followers`, {
194192
headers: { 'Accept': 'text/html' }
195193
});
196194

@@ -279,7 +277,8 @@ $(() => {
279277
const postId = $tgt.data('post');
280278

281279
if (!pingable[`${threadId}-${postId}`] || Object.keys(pingable[`${threadId}-${postId}`]).length === 0) {
282-
const resp = await fetch(`/comments/thread/${threadId}/pingable?post=${postId}`);
280+
const resp = await QPixel.getJSON(`/comments/thread/${threadId}/pingable?post=${postId}`);
281+
283282
pingable[`${threadId}-${postId}`] = await resp.json();
284283
}
285284

app/assets/javascripts/notifications.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,11 @@ $(() => {
4545
}
4646
};
4747

48-
$('.inbox-toggle').on('click', async (evt) => {
49-
evt.preventDefault();
48+
$('.inbox-toggle').on('click', async (ev) => {
49+
ev.preventDefault();
5050
const $inbox = $('.inbox');
5151
if($inbox.hasClass("is-active")) {
52-
const resp = await fetch(`/users/me/notifications`, {
53-
credentials: 'include',
54-
headers: { 'Accept': 'application/json' }
55-
});
52+
const resp = await QPixel.getJSON(`/users/me/notifications`);
5653

5754
const data = await resp.json();
5855
const $inboxContainer = $inbox.find(".inbox--container");

app/assets/javascripts/qpixel_api.js

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,8 @@ window.QPixel = {
130130
return QPixel._pendingUserResponse;
131131
}
132132

133-
const myselfPromise = fetch('/users/me', {
134-
credentials: 'include',
135-
headers: {
136-
'Accept': 'application/json'
137-
}
133+
const myselfPromise = QPixel.fetch('/users/me', {
134+
headers: { 'Accept': 'application/json' }
138135
});
139136

140137
QPixel._pendingUserResponse = myselfPromise;
@@ -219,12 +216,7 @@ window.QPixel = {
219216
filters: async () => {
220217
if (this._filters == null) {
221218
// If they're still absent after loading from storage, load from the API.
222-
const resp = await fetch('/users/me/filters', {
223-
credentials: 'include',
224-
headers: {
225-
'Accept': 'application/json'
226-
}
227-
});
219+
const resp = await QPixel.getJSON('/users/me/filters');
228220
const data = await resp.json();
229221

230222
QPixel.Storage?.set('user_filters', data);
@@ -240,13 +232,8 @@ window.QPixel = {
240232
if (!user) {
241233
return '';
242234
}
243-
244-
const resp = await fetch(`/users/me/filters/default?category=${categoryId}`, {
245-
credentials: 'include',
246-
headers: {
247-
'Accept': 'application/json'
248-
}
249-
});
235+
236+
const resp = await QPixel.getJSON(`/users/me/filters/default?category=${categoryId}`);
250237

251238
const data = await resp.json();
252239
return data.name;
@@ -313,12 +300,7 @@ window.QPixel = {
313300
},
314301

315302
_fetchPreferences: async () => {
316-
const resp = await fetch('/users/me/preferences', {
317-
credentials: 'include',
318-
headers: {
319-
'Accept': 'application/json'
320-
}
321-
});
303+
const resp = await QPixel.getJSON('/users/me/preferences');
322304
const data = await resp.json();
323305
QPixel._updatePreferencesLocally(data);
324306
},
@@ -343,41 +325,56 @@ window.QPixel = {
343325
return [currentSequence, posInSeq];
344326
},
345327

346-
fetchJSON: async (uri, data, options) => {
328+
fetch: async (uri, init) => {
347329
const defaultHeaders = {
348330
'X-CSRF-Token': QPixel.csrfToken(),
331+
// X-Requested-With is necessary for request.xhr? to work
332+
'X-Requested-With': 'XMLHttpRequest',
349333
'Content-Type': 'application/json',
350334
};
351335

352-
const { headers = {}, ...otherOptions } = options ?? {};
336+
const { headers = {}, ...restInit } = init ?? {};
337+
338+
/** @type {RequestInit} */
339+
const requestInit = {
340+
headers: {
341+
...defaultHeaders,
342+
...headers,
343+
},
344+
credentials: 'include',
345+
...restInit,
346+
};
353347

348+
return fetch(uri, requestInit);
349+
},
350+
351+
fetchJSON: async (uri, data, options = {}) => {
354352
/** @type {RequestInit} */
355353
const requestInit = {
356354
method: 'POST',
357-
headers: {
358-
...defaultHeaders,
359-
...headers,
360-
},
361-
credentials: 'include',
362-
body: otherOptions.method === 'GET' ? void 0 : JSON.stringify(data),
363-
...otherOptions,
355+
body: options.method === 'GET' ? void 0 : JSON.stringify(data),
356+
...options,
364357
};
365358

366-
return fetch(uri, requestInit);
359+
return QPixel.fetch(uri, requestInit);
367360
},
368361

369362
getJSON: async (uri, options = {}) => {
363+
const { headers = {} } = options ?? {};
364+
370365
return QPixel.fetchJSON(uri, {}, {
371366
...options,
367+
headers: {
368+
'Accept': 'application/json',
369+
'X-Requested-With': 'XMLHttpRequest',
370+
...headers,
371+
},
372372
method: 'GET',
373373
});
374374
},
375375

376376
getComment: async (id) => {
377-
const resp = await fetch(`/comments/${id}`, {
378-
credentials: 'include',
379-
headers: { 'Accept': 'application/json' }
380-
});
377+
const resp = await QPixel.getJSON(`/comments/${id}`);
381378

382379
const data = await resp.json();
383380

@@ -392,7 +389,7 @@ window.QPixel = {
392389
url.searchParams.append('inline', `${inline}`);
393390
url.searchParams.append('show_deleted_comments', `${showDeleted ? 1 : 0}`);
394391

395-
const resp = await fetch(url.toString(), {
392+
const resp = await QPixel.fetch(url.toString(), {
396393
headers: { 'Accept': 'text/html' }
397394
});
398395

@@ -404,7 +401,7 @@ window.QPixel = {
404401
getThreadsListContent: async (id) => {
405402
const url = new URL(`/comments/post/${id}`, window.location.origin);
406403

407-
const resp = await fetch(url.toString(), {
404+
const resp = await QPixel.fetch(url.toString(), {
408405
headers: { 'Accept': 'text/html' }
409406
});
410407

app/assets/javascripts/search.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
$(() => {
2-
let postTypes;
2+
/** @type {QPixelPostType[] | null} */
3+
let postTypes = null;
34
const $itemTemplate = $('<a href="javascript:void(0)" class="item"></a>');
45

56
$(document).on('keyup', 'input[name="search"]', async (ev) => {
@@ -32,18 +33,15 @@ $(() => {
3233
};
3334

3435
if (!postTypes) {
35-
const resp = await fetch(`/posts/types`, {
36-
method: 'GET',
37-
headers: {
38-
'Accept': 'application/json'
39-
}
40-
});
36+
const resp = await QPixel.getJSON(`/posts/types`);
37+
4138
postTypes = await resp.json();
4239
}
4340

4441
const items = postTypes.filter((pt) => pt.name.startsWith(currentWord.substr(10))).map((pt) => {
4542
return $itemTemplate.clone().text(pt.name).attr('data-post-type-id', pt.id);
4643
});
44+
4745
QPixel.Popup.getPopup(items, $tgt[0], callback);
4846
});
4947
});

app/assets/javascripts/site_settings.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ $(() => {
2727
const valueType = $tgt.data('type');
2828
const communityId = $tgt.data('community-id');
2929

30-
const resp = await fetch(`/admin/settings/${name}${!!communityId ? '?community_id=' + communityId : ''}`, {
31-
credentials: 'include'
32-
});
30+
const resp = await QPixel.getJSON(`/admin/settings/${name}${!!communityId ? '?community_id=' + communityId : ''}`);
3331

3432
const data = await resp.json();
3533
const value = data.typed;

app/assets/javascripts/tag_sets.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ $(() => {
33
const $tgt = $(ev.target);
44
const tagSetId = $tgt.data('set-id');
55

6-
const response = await fetch(`/admin/tag-sets/${tagSetId}`, {
7-
headers: { 'Accept': 'application/json' }
8-
});
6+
const resp = await QPixel.getJSON(`/admin/tag-sets/${tagSetId}`);
97

10-
const data = await response.json();
8+
const data = await resp.json();
119

1210
const name = data.name;
1311
const $form = `<input type="text" class="js-edit-set-name form-element" value="${name}" />

global.d.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,26 @@ interface GetThreadContentOptions {
270270
showDeleted?: boolean
271271
}
272272

273+
type QPixelPostType = {
274+
id: number
275+
name: string
276+
description: string | null
277+
has_answers: boolean
278+
has_votes: boolean
279+
has_tags: boolean
280+
has_parent: boolean
281+
has_category: boolean
282+
has_license: boolean
283+
is_public_editable: boolean
284+
is_closeable: boolean
285+
is_top_level: boolean
286+
is_freely_editable: boolean
287+
icon_name: string | null
288+
has_reactions: boolean
289+
answer_type_id: number | null
290+
has_only_specific_reactions: boolean
291+
}
292+
273293
interface QPixel {
274294
// constants
275295

@@ -423,6 +443,13 @@ interface QPixel {
423443
*/
424444
validatePost?: (postText: string) => [boolean, PostValidatorMessage[]];
425445

446+
/**
447+
* Wrapper around {@link fetch} to ensure credentials, CSRF token, and X-Requested-With are always sent
448+
* @param uri target URI of the request
449+
* @param options options to pass to {@link fetch}
450+
*/
451+
fetch?: (uri: string | URL, options?: RequestInit) => Promise<Response>
452+
426453
/**
427454
* Send a request with JSON data, pre-authorized with QPixel credentials for the signed in user.
428455
* @param uri The URI to which to send the request.
@@ -435,7 +462,6 @@ interface QPixel {
435462
/**
436463
* @param uri The URI to which to send the request.
437464
* @param options An optional {@link RequestInit} to override the defaults provided by {@link fetchJSON}
438-
* @returns
439465
*/
440466
getJSON?: (uri: string, options?: Omit<RequestInit, 'method'>) => Promise<Response>;
441467

0 commit comments

Comments
 (0)