Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ overrides:
- files: ["*.config.*"]
rules:
import/no-unused-modules: [0]
- files: ["web_src/js/modules/fetch.js", "web_src/js/standalone/**/*"]
rules:
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]

rules:
"@eslint-community/eslint-comments/disable-enable-pair": [2]
Expand Down Expand Up @@ -420,7 +423,7 @@ rules:
no-restricted-exports: [0]
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
no-restricted-imports: [0]
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.js instead"}]
no-return-assign: [0]
no-script-url: [2]
no-self-assign: [2, {props: true}]
Expand Down
5 changes: 3 additions & 2 deletions web_src/js/components/DashboardRepoList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {createApp, nextTick} from 'vue';
import $ from 'jquery';
import {SvgIcon} from '../svg.js';
import {GET} from '../modules/fetch.js';
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
Expand Down Expand Up @@ -233,11 +234,11 @@ const sfc = {
try {
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
response = await fetch(totalCountSearchURL);
response = await GET(totalCountSearchURL);
this.reposTotalCount = response.headers.get('X-Total-Count');
}
response = await fetch(searchedURL);
response = await GET(searchedURL);
json = await response.json();
} catch {
if (searchedURL === this.searchURL) {
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/components/DiffCommitSelector.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script>
import {SvgIcon} from '../svg.js';
import {GET} from '../modules/fetch.js';
export default {
components: {SvgIcon},
Expand Down Expand Up @@ -123,7 +124,7 @@ export default {
},
/** Load the commits to show in this dropdown */
async fetchCommits() {
const resp = await fetch(`${this.issueLink}/commits/list`);
const resp = await GET(`${this.issueLink}/commits/list`);
const results = await resp.json();
this.commits.push(...results.commits.map((x) => {
x.hovered = false;
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/components/RepoBranchTagSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import $ from 'jquery';
import {SvgIcon} from '../svg.js';
import {pathEscapeSegments} from '../utils/url.js';
import {showErrorToast} from '../modules/toast.js';
import {GET} from '../modules/fetch.js';

const sfc = {
components: {SvgIcon},
Expand Down Expand Up @@ -191,8 +192,7 @@ const sfc = {
this.isLoading = true;
try {
// the "data.defaultBranch" is ambiguous, it could be "branch name" or "tag name"
const reqUrl = `${this.repoLink}/${this.mode}/list`;
const resp = await fetch(reqUrl);
const resp = await GET(`${this.repoLink}/${this.mode}/list`);
const {results} = await resp.json();
for (const result of results) {
let selected = false;
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/common-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {htmlEscape} from 'escape-goat';
import {showTemporaryTooltip} from '../modules/tippy.js';
import {confirmModal} from './comp/ConfirmModal.js';
import {showErrorToast} from '../modules/toast.js';
import {request} from '../modules/fetch.js';

const {appUrl, appSubUrl, csrfToken, i18n} = window.config;

Expand Down Expand Up @@ -81,7 +82,7 @@ function fetchActionDoRedirect(redirect) {

async function fetchActionDoRequest(actionElem, url, opt) {
try {
const resp = await fetch(url, opt);
const resp = await request(url, opt);
if (resp.status === 200) {
let {redirect} = await resp.json();
redirect = redirect || actionElem.getAttribute('data-redirect');
Expand Down
5 changes: 3 additions & 2 deletions web_src/js/features/common-issue-list.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import $ from 'jquery';
import {isElemHidden, onInputDebounce, toggleElem} from '../utils/dom.js';
const {appSubUrl} = window.config;
import {GET} from '../modules/fetch.js';

const {appSubUrl} = window.config;
const reIssueIndex = /^(\d+)$/; // eg: "123"
const reIssueSharpIndex = /^#(\d+)$/; // eg: "#123"
const reIssueOwnerRepoIndex = /^([-.\w]+)\/([-.\w]+)#(\d+)$/; // eg: "{owner}/{repo}#{index}"
Expand Down Expand Up @@ -54,7 +55,7 @@ export function initCommonIssueListQuickGoto() {
// try to check whether the parsed goto link is valid
let targetUrl = parseIssueListQuickGotoLink(repoLink, searchText);
if (targetUrl) {
const res = await fetch(`${targetUrl}/info`);
const res = await GET(`${targetUrl}/info`);
if (res.status !== 200) targetUrl = '';
}

Expand Down
9 changes: 2 additions & 7 deletions web_src/js/features/comp/ImagePaste.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import $ from 'jquery';

const {csrfToken} = window.config;
import {POST} from '../../modules/fetch.js';

async function uploadFile(file, uploadUrl) {
const formData = new FormData();
formData.append('file', file, file.name);

const res = await fetch(uploadUrl, {
method: 'POST',
headers: {'X-Csrf-Token': csrfToken},
body: formData,
});
const res = await POST(uploadUrl, {data: formData});
return await res.json();
}

Expand Down
14 changes: 3 additions & 11 deletions web_src/js/features/comp/ReactionSelector.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import $ from 'jquery';

const {csrfToken} = window.config;
import {POST} from '../../modules/fetch.js';

export function initCompReactionSelector($parent) {
$parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) {
Expand All @@ -12,15 +11,8 @@ export function initCompReactionSelector($parent) {
const reactionContent = $(this).attr('data-reaction-content');
const hasReacted = $(this).closest('.ui.segment.reactions').find(`a[data-reaction-content="${reactionContent}"]`).attr('data-has-reacted') === 'true';

const res = await fetch(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
_csrf: csrfToken,
content: reactionContent,
}),
const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
data: new URLSearchParams({content: reactionContent}),
});

const data = await res.json();
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/copycontent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {clippie} from 'clippie';
import {showTemporaryTooltip} from '../modules/tippy.js';
import {convertImage} from '../utils.js';
import {GET} from '../modules/fetch.js';

const {i18n} = window.config;

Expand All @@ -20,7 +21,7 @@ export function initCopyContent() {
if (link) {
btn.classList.add('is-loading', 'small-loading-icon');
try {
const res = await fetch(link, {credentials: 'include', redirect: 'follow'});
const res = await GET(link, {credentials: 'include', redirect: 'follow'});
const contentType = res.headers.get('content-type');

if (contentType.startsWith('image/') && !contentType.startsWith('image/svg')) {
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/install.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from 'jquery';
import {hideElem, showElem} from '../utils/dom.js';
import {GET} from '../modules/fetch.js';

export function initInstall() {
const $page = $('.page-content.install');
Expand Down Expand Up @@ -111,7 +112,7 @@ function initPostInstall() {
const targetUrl = el.getAttribute('href');
let tid = setInterval(async () => {
try {
const resp = await fetch(targetUrl);
const resp = await GET(targetUrl);
if (tid && resp.status === 200) {
clearInterval(tid);
tid = null;
Expand Down
9 changes: 3 additions & 6 deletions web_src/js/features/pull-view-file.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {diffTreeStore} from '../modules/stores.js';
import {setFileFolding} from './file-fold.js';
import {POST} from '../modules/fetch.js';

const {csrfToken, pageData} = window.config;
const {pageData} = window.config;
const prReview = pageData.prReview || {};
const viewedStyleClass = 'viewed-file-checked-form';
const viewedCheckboxSelector = '.viewed-file-form'; // Selector under which all "Viewed" checkbox forms can be found
Expand Down Expand Up @@ -68,11 +69,7 @@ export function initViewedCheckboxListenerFor() {
const data = {files};
const headCommitSHA = form.getAttribute('data-headcommit');
if (headCommitSHA) data.headCommitSHA = headCommitSHA;
fetch(form.getAttribute('data-link'), {
method: 'POST',
headers: {'X-Csrf-Token': csrfToken},
body: JSON.stringify(data),
});
POST(form.getAttribute('data-link'), {data});

// Fold the file accordingly
const parentBox = form.closest('.diff-file-header');
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/repo-diff-commit.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
import {GET} from '../modules/fetch.js';

async function loadBranchesAndTags(area, loadingButton) {
loadingButton.classList.add('disabled');
try {
const res = await fetch(loadingButton.getAttribute('data-fetch-url'));
const res = await GET(loadingButton.getAttribute('data-fetch-url'));
const data = await res.json();
hideElem(loadingButton);
addTags(area, data.tags);
Expand Down
14 changes: 3 additions & 11 deletions web_src/js/features/repo-issue-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {htmlEscape} from 'escape-goat';
import {confirmModal} from './comp/ConfirmModal.js';
import {showErrorToast} from '../modules/toast.js';
import {createSortable} from '../modules/sortable.js';
import {DELETE, POST} from '../modules/fetch.js';

function initRepoIssueListCheckboxes() {
const $issueSelectAll = $('.issue-checkbox-all');
Expand Down Expand Up @@ -146,10 +147,8 @@ function initPinRemoveButton() {
const id = Number(el.getAttribute('data-issue-id'));

// Send the unpin request
const response = await fetch(el.getAttribute('data-unpin-url'), {
method: 'delete',
const response = await DELETE(el.getAttribute('data-unpin-url'), {
headers: {
'X-Csrf-Token': window.config.csrfToken,
'Content-Type': 'application/json',
},
});
Expand All @@ -166,14 +165,7 @@ function initPinRemoveButton() {
async function pinMoveEnd(e) {
const url = e.item.getAttribute('data-move-url');
const id = Number(e.item.getAttribute('data-issue-id'));
await fetch(url, {
method: 'post',
body: JSON.stringify({id, position: e.newIndex + 1}),
headers: {
'X-Csrf-Token': window.config.csrfToken,
'Content-Type': 'application/json',
},
});
await POST(url, {data: {id, position: e.newIndex + 1}});
}

async function initIssuePinSort() {
Expand Down
9 changes: 4 additions & 5 deletions web_src/js/features/repo-migrate.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import $ from 'jquery';
import {hideElem, showElem} from '../utils/dom.js';
import {GET, POST} from '../modules/fetch.js';

const {appSubUrl, csrfToken} = window.config;
const {appSubUrl} = window.config;

export function initRepoMigrationStatusChecker() {
const $repoMigrating = $('#repo_migrating');
Expand All @@ -13,7 +14,7 @@ export function initRepoMigrationStatusChecker() {

// returns true if the refresh still need to be called after a while
const refresh = async () => {
const res = await fetch(`${appSubUrl}/user/task/${task}`);
const res = await GET(`${appSubUrl}/user/task/${task}`);
if (res.status !== 200) return true; // continue to refresh if network error occurs

const data = await res.json();
Expand Down Expand Up @@ -58,10 +59,8 @@ export function initRepoMigrationStatusChecker() {
}

async function doMigrationRetry(e) {
await fetch($(e.target).attr('data-migrating-task-retry-url'), {
method: 'post',
await POST($(e.target).attr('data-migrating-task-retry-url'), {
headers: {
'X-Csrf-Token': csrfToken,
'Content-Type': 'application/json',
},
});
Expand Down
38 changes: 13 additions & 25 deletions web_src/js/features/user-auth-webauthn.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.js';
import {showElem} from '../utils/dom.js';
import {GET, POST} from '../modules/fetch.js';

const {appSubUrl, csrfToken} = window.config;
const {appSubUrl} = window.config;

export async function initUserAuthWebAuthn() {
const elPrompt = document.querySelector('.user.signin.webauthn-prompt');
Expand All @@ -13,7 +14,7 @@ export async function initUserAuthWebAuthn() {
return;
}

const res = await fetch(`${appSubUrl}/user/webauthn/assertion`);
const res = await GET(`${appSubUrl}/user/webauthn/assertion`);
if (res.status !== 200) {
webAuthnError('unknown');
return;
Expand Down Expand Up @@ -53,12 +54,8 @@ async function verifyAssertion(assertedCredential) {
const sig = new Uint8Array(assertedCredential.response.signature);
const userHandle = new Uint8Array(assertedCredential.response.userHandle);

const res = await fetch(`${appSubUrl}/user/webauthn/assertion`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify({
const res = await POST(`${appSubUrl}/user/webauthn/assertion`, {
data: {
id: assertedCredential.id,
rawId: encodeURLEncodedBase64(rawId),
type: assertedCredential.type,
Expand All @@ -69,7 +66,7 @@ async function verifyAssertion(assertedCredential) {
signature: encodeURLEncodedBase64(sig),
userHandle: encodeURLEncodedBase64(userHandle),
},
}),
},
});
if (res.status === 500) {
webAuthnError('unknown');
Expand All @@ -88,21 +85,16 @@ async function webauthnRegistered(newCredential) {
const clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON);
const rawId = new Uint8Array(newCredential.rawId);

const res = await fetch(`${appSubUrl}/user/settings/security/webauthn/register`, {
method: 'POST',
headers: {
'X-Csrf-Token': csrfToken,
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify({
const res = await POST(`${appSubUrl}/user/settings/security/webauthn/register`, {
data: {
id: newCredential.id,
rawId: encodeURLEncodedBase64(rawId),
type: newCredential.type,
response: {
attestationObject: encodeURLEncodedBase64(attestationObject),
clientDataJSON: encodeURLEncodedBase64(clientDataJSON),
},
}),
},
});

if (res.status === 409) {
Expand Down Expand Up @@ -165,15 +157,11 @@ export function initUserAuthWebAuthnRegister() {
async function webAuthnRegisterRequest() {
const elNickname = document.getElementById('nickname');

const body = new FormData();
body.append('name', elNickname.value);
const formData = new FormData();
formData.append('name', elNickname.value);

const res = await fetch(`${appSubUrl}/user/settings/security/webauthn/request_register`, {
method: 'POST',
headers: {
'X-Csrf-Token': csrfToken,
},
body,
const res = await POST(`${appSubUrl}/user/settings/security/webauthn/request_register`, {
data: formData,
});

if (res.status === 409) {
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/modules/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const {csrfToken} = window.config;
// fetch wrapper, use below method name functions and the `data` option to pass in data
// which will automatically set an appropriate content-type header. For json content,
// only object and array types are currently supported.
function request(url, {headers, data, body, ...other} = {}) {
export function request(url, {headers, data, body, ...other} = {}) {
let contentType;
if (!body) {
if (data instanceof FormData) {
Expand All @@ -14,6 +14,7 @@ function request(url, {headers, data, body, ...other} = {}) {
} else if (data instanceof URLSearchParams) {
contentType = 'application/x-www-form-urlencoded';
body = data;
body.set('_csrf', csrfToken);
} else if (isObject(data) || Array.isArray(data)) {
contentType = 'application/json';
body = JSON.stringify(data);
Expand Down