mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 10:56:10 +01:00 
			
		
		
		
	Use fetch helpers instead of fetch (#27026)
WIP because: - [x] Some calls set a `content-type` but send no body, can likely remove the header - [x] Need to check whether `charset=utf-8` has any significance on the webauthn calls, I assume not as it is the default for json content. - [x] Maybe `no-restricted-globals` is better for eslint, but will require a lot of duplication in the yaml or moving eslint config to a `.js` extension. - [x] Maybe export `request` as `fetch`, shadowing the global.
This commit is contained in:
		| @@ -46,6 +46,9 @@ overrides: | |||||||
|   - files: ["*.config.*"] |   - files: ["*.config.*"] | ||||||
|     rules: |     rules: | ||||||
|       import/no-unused-modules: [0] |       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: | rules: | ||||||
|   "@eslint-community/eslint-comments/disable-enable-pair": [2] |   "@eslint-community/eslint-comments/disable-enable-pair": [2] | ||||||
| @@ -420,7 +423,7 @@ rules: | |||||||
|   no-restricted-exports: [0] |   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-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-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-return-assign: [0] | ||||||
|   no-script-url: [2] |   no-script-url: [2] | ||||||
|   no-self-assign: [2, {props: true}] |   no-self-assign: [2, {props: true}] | ||||||
|   | |||||||
| @@ -95,7 +95,7 @@ Some lint rules and IDEs also have warnings if the returned Promise is not handl | |||||||
| ### Fetching data | ### Fetching data | ||||||
|  |  | ||||||
| To fetch data, use the wrapper functions `GET`, `POST` etc. from `modules/fetch.js`. They | To fetch data, use the wrapper functions `GET`, `POST` etc. from `modules/fetch.js`. They | ||||||
| accept a `data` option for the content, will automatically set CSFR token and return a | accept a `data` option for the content, will automatically set CSRF token and return a | ||||||
| Promise for a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). | Promise for a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). | ||||||
|  |  | ||||||
| ### HTML Attributes and `dataset` | ### HTML Attributes and `dataset` | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| import {createApp, nextTick} from 'vue'; | import {createApp, nextTick} from 'vue'; | ||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {SvgIcon} from '../svg.js'; | import {SvgIcon} from '../svg.js'; | ||||||
|  | import {GET} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| const {appSubUrl, assetUrlPrefix, pageData} = window.config; | const {appSubUrl, assetUrlPrefix, pageData} = window.config; | ||||||
|  |  | ||||||
| @@ -233,11 +234,11 @@ const sfc = { | |||||||
|       try { |       try { | ||||||
|         if (!this.reposTotalCount) { |         if (!this.reposTotalCount) { | ||||||
|           const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`; |           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'); |           this.reposTotalCount = response.headers.get('X-Total-Count'); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         response = await fetch(searchedURL); |         response = await GET(searchedURL); | ||||||
|         json = await response.json(); |         json = await response.json(); | ||||||
|       } catch { |       } catch { | ||||||
|         if (searchedURL === this.searchURL) { |         if (searchedURL === this.searchURL) { | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| <script> | <script> | ||||||
| import {SvgIcon} from '../svg.js'; | import {SvgIcon} from '../svg.js'; | ||||||
|  | import {GET} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   components: {SvgIcon}, |   components: {SvgIcon}, | ||||||
| @@ -123,7 +124,7 @@ export default { | |||||||
|     }, |     }, | ||||||
|     /** Load the commits to show in this dropdown */ |     /** Load the commits to show in this dropdown */ | ||||||
|     async fetchCommits() { |     async fetchCommits() { | ||||||
|       const resp = await fetch(`${this.issueLink}/commits/list`); |       const resp = await GET(`${this.issueLink}/commits/list`); | ||||||
|       const results = await resp.json(); |       const results = await resp.json(); | ||||||
|       this.commits.push(...results.commits.map((x) => { |       this.commits.push(...results.commits.map((x) => { | ||||||
|         x.hovered = false; |         x.hovered = false; | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import $ from 'jquery'; | |||||||
| import {SvgIcon} from '../svg.js'; | import {SvgIcon} from '../svg.js'; | ||||||
| import {pathEscapeSegments} from '../utils/url.js'; | import {pathEscapeSegments} from '../utils/url.js'; | ||||||
| import {showErrorToast} from '../modules/toast.js'; | import {showErrorToast} from '../modules/toast.js'; | ||||||
|  | import {GET} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| const sfc = { | const sfc = { | ||||||
|   components: {SvgIcon}, |   components: {SvgIcon}, | ||||||
| @@ -190,8 +191,7 @@ const sfc = { | |||||||
|       } |       } | ||||||
|       this.isLoading = true; |       this.isLoading = true; | ||||||
|       try { |       try { | ||||||
|         const reqUrl = `${this.repoLink}/${this.mode}/list`; |         const resp = await GET(`${this.repoLink}/${this.mode}/list`); | ||||||
|         const resp = await fetch(reqUrl); |  | ||||||
|         const {results} = await resp.json(); |         const {results} = await resp.json(); | ||||||
|         for (const result of results) { |         for (const result of results) { | ||||||
|           let selected = false; |           let selected = false; | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import {htmlEscape} from 'escape-goat'; | |||||||
| import {showTemporaryTooltip} from '../modules/tippy.js'; | import {showTemporaryTooltip} from '../modules/tippy.js'; | ||||||
| import {confirmModal} from './comp/ConfirmModal.js'; | import {confirmModal} from './comp/ConfirmModal.js'; | ||||||
| import {showErrorToast} from '../modules/toast.js'; | import {showErrorToast} from '../modules/toast.js'; | ||||||
|  | import {request} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| const {appUrl, appSubUrl, csrfToken, i18n} = window.config; | const {appUrl, appSubUrl, csrfToken, i18n} = window.config; | ||||||
|  |  | ||||||
| @@ -81,7 +82,7 @@ function fetchActionDoRedirect(redirect) { | |||||||
|  |  | ||||||
| async function fetchActionDoRequest(actionElem, url, opt) { | async function fetchActionDoRequest(actionElem, url, opt) { | ||||||
|   try { |   try { | ||||||
|     const resp = await fetch(url, opt); |     const resp = await request(url, opt); | ||||||
|     if (resp.status === 200) { |     if (resp.status === 200) { | ||||||
|       let {redirect} = await resp.json(); |       let {redirect} = await resp.json(); | ||||||
|       redirect = redirect || actionElem.getAttribute('data-redirect'); |       redirect = redirect || actionElem.getAttribute('data-redirect'); | ||||||
| @@ -127,7 +128,7 @@ async function formFetchAction(e) { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   let reqUrl = formActionUrl; |   let reqUrl = formActionUrl; | ||||||
|   const reqOpt = {method: formMethod.toUpperCase(), headers: {'X-Csrf-Token': csrfToken}}; |   const reqOpt = {method: formMethod.toUpperCase()}; | ||||||
|   if (formMethod.toLowerCase() === 'get') { |   if (formMethod.toLowerCase() === 'get') { | ||||||
|     const params = new URLSearchParams(); |     const params = new URLSearchParams(); | ||||||
|     for (const [key, value] of formData) { |     for (const [key, value] of formData) { | ||||||
| @@ -264,7 +265,7 @@ async function linkAction(e) { | |||||||
|   const url = el.getAttribute('data-url'); |   const url = el.getAttribute('data-url'); | ||||||
|   const doRequest = async () => { |   const doRequest = async () => { | ||||||
|     el.disabled = true; |     el.disabled = true; | ||||||
|     await fetchActionDoRequest(el, url, {method: 'POST', headers: {'X-Csrf-Token': csrfToken}}); |     await fetchActionDoRequest(el, url, {method: 'POST'}); | ||||||
|     el.disabled = false; |     el.disabled = false; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {isElemHidden, onInputDebounce, toggleElem} from '../utils/dom.js'; | 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 reIssueIndex = /^(\d+)$/; // eg: "123" | ||||||
| const reIssueSharpIndex = /^#(\d+)$/; // eg: "#123" | const reIssueSharpIndex = /^#(\d+)$/; // eg: "#123" | ||||||
| const reIssueOwnerRepoIndex = /^([-.\w]+)\/([-.\w]+)#(\d+)$/;  // eg: "{owner}/{repo}#{index}" | const reIssueOwnerRepoIndex = /^([-.\w]+)\/([-.\w]+)#(\d+)$/;  // eg: "{owner}/{repo}#{index}" | ||||||
| @@ -54,7 +55,7 @@ export function initCommonIssueListQuickGoto() { | |||||||
|     // try to check whether the parsed goto link is valid |     // try to check whether the parsed goto link is valid | ||||||
|     let targetUrl = parseIssueListQuickGotoLink(repoLink, searchText); |     let targetUrl = parseIssueListQuickGotoLink(repoLink, searchText); | ||||||
|     if (targetUrl) { |     if (targetUrl) { | ||||||
|       const res = await fetch(`${targetUrl}/info`); |       const res = await GET(`${targetUrl}/info`); | ||||||
|       if (res.status !== 200) targetUrl = ''; |       if (res.status !== 200) targetUrl = ''; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,16 +1,11 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
|  | import {POST} from '../../modules/fetch.js'; | ||||||
| const {csrfToken} = window.config; |  | ||||||
|  |  | ||||||
| async function uploadFile(file, uploadUrl) { | async function uploadFile(file, uploadUrl) { | ||||||
|   const formData = new FormData(); |   const formData = new FormData(); | ||||||
|   formData.append('file', file, file.name); |   formData.append('file', file, file.name); | ||||||
|  |  | ||||||
|   const res = await fetch(uploadUrl, { |   const res = await POST(uploadUrl, {data: formData}); | ||||||
|     method: 'POST', |  | ||||||
|     headers: {'X-Csrf-Token': csrfToken}, |  | ||||||
|     body: formData, |  | ||||||
|   }); |  | ||||||
|   return await res.json(); |   return await res.json(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
|  | import {POST} from '../../modules/fetch.js'; | ||||||
| const {csrfToken} = window.config; |  | ||||||
|  |  | ||||||
| export function initCompReactionSelector($parent) { | export function initCompReactionSelector($parent) { | ||||||
|   $parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) { |   $parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) { | ||||||
| @@ -12,15 +11,8 @@ export function initCompReactionSelector($parent) { | |||||||
|     const reactionContent = $(this).attr('data-reaction-content'); |     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 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'}`, { |     const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, { | ||||||
|       method: 'POST', |       data: new URLSearchParams({content: reactionContent}), | ||||||
|       headers: { |  | ||||||
|         'content-type': 'application/x-www-form-urlencoded', |  | ||||||
|       }, |  | ||||||
|       body: new URLSearchParams({ |  | ||||||
|         _csrf: csrfToken, |  | ||||||
|         content: reactionContent, |  | ||||||
|       }), |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     const data = await res.json(); |     const data = await res.json(); | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import {clippie} from 'clippie'; | import {clippie} from 'clippie'; | ||||||
| import {showTemporaryTooltip} from '../modules/tippy.js'; | import {showTemporaryTooltip} from '../modules/tippy.js'; | ||||||
| import {convertImage} from '../utils.js'; | import {convertImage} from '../utils.js'; | ||||||
|  | import {GET} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| const {i18n} = window.config; | const {i18n} = window.config; | ||||||
|  |  | ||||||
| @@ -20,7 +21,7 @@ export function initCopyContent() { | |||||||
|     if (link) { |     if (link) { | ||||||
|       btn.classList.add('is-loading', 'small-loading-icon'); |       btn.classList.add('is-loading', 'small-loading-icon'); | ||||||
|       try { |       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'); |         const contentType = res.headers.get('content-type'); | ||||||
|  |  | ||||||
|         if (contentType.startsWith('image/') && !contentType.startsWith('image/svg')) { |         if (contentType.startsWith('image/') && !contentType.startsWith('image/svg')) { | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {hideElem, showElem} from '../utils/dom.js'; | import {hideElem, showElem} from '../utils/dom.js'; | ||||||
|  | import {GET} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| export function initInstall() { | export function initInstall() { | ||||||
|   const $page = $('.page-content.install'); |   const $page = $('.page-content.install'); | ||||||
| @@ -111,7 +112,7 @@ function initPostInstall() { | |||||||
|   const targetUrl = el.getAttribute('href'); |   const targetUrl = el.getAttribute('href'); | ||||||
|   let tid = setInterval(async () => { |   let tid = setInterval(async () => { | ||||||
|     try { |     try { | ||||||
|       const resp = await fetch(targetUrl); |       const resp = await GET(targetUrl); | ||||||
|       if (tid && resp.status === 200) { |       if (tid && resp.status === 200) { | ||||||
|         clearInterval(tid); |         clearInterval(tid); | ||||||
|         tid = null; |         tid = null; | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| import {diffTreeStore} from '../modules/stores.js'; | import {diffTreeStore} from '../modules/stores.js'; | ||||||
| import {setFileFolding} from './file-fold.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 prReview = pageData.prReview || {}; | ||||||
| const viewedStyleClass = 'viewed-file-checked-form'; | const viewedStyleClass = 'viewed-file-checked-form'; | ||||||
| const viewedCheckboxSelector = '.viewed-file-form'; // Selector under which all "Viewed" checkbox forms can be found | const viewedCheckboxSelector = '.viewed-file-form'; // Selector under which all "Viewed" checkbox forms can be found | ||||||
| @@ -68,11 +69,7 @@ export function initViewedCheckboxListenerFor() { | |||||||
|       const data = {files}; |       const data = {files}; | ||||||
|       const headCommitSHA = form.getAttribute('data-headcommit'); |       const headCommitSHA = form.getAttribute('data-headcommit'); | ||||||
|       if (headCommitSHA) data.headCommitSHA = headCommitSHA; |       if (headCommitSHA) data.headCommitSHA = headCommitSHA; | ||||||
|       fetch(form.getAttribute('data-link'), { |       POST(form.getAttribute('data-link'), {data}); | ||||||
|         method: 'POST', |  | ||||||
|         headers: {'X-Csrf-Token': csrfToken}, |  | ||||||
|         body: JSON.stringify(data), |  | ||||||
|       }); |  | ||||||
|  |  | ||||||
|       // Fold the file accordingly |       // Fold the file accordingly | ||||||
|       const parentBox = form.closest('.diff-file-header'); |       const parentBox = form.closest('.diff-file-header'); | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| import {hideElem, showElem, toggleElem} from '../utils/dom.js'; | import {hideElem, showElem, toggleElem} from '../utils/dom.js'; | ||||||
|  | import {GET} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| async function loadBranchesAndTags(area, loadingButton) { | async function loadBranchesAndTags(area, loadingButton) { | ||||||
|   loadingButton.classList.add('disabled'); |   loadingButton.classList.add('disabled'); | ||||||
|   try { |   try { | ||||||
|     const res = await fetch(loadingButton.getAttribute('data-fetch-url')); |     const res = await GET(loadingButton.getAttribute('data-fetch-url')); | ||||||
|     const data = await res.json(); |     const data = await res.json(); | ||||||
|     hideElem(loadingButton); |     hideElem(loadingButton); | ||||||
|     addTags(area, data.tags); |     addTags(area, data.tags); | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import {htmlEscape} from 'escape-goat'; | |||||||
| import {confirmModal} from './comp/ConfirmModal.js'; | import {confirmModal} from './comp/ConfirmModal.js'; | ||||||
| import {showErrorToast} from '../modules/toast.js'; | import {showErrorToast} from '../modules/toast.js'; | ||||||
| import {createSortable} from '../modules/sortable.js'; | import {createSortable} from '../modules/sortable.js'; | ||||||
|  | import {DELETE, POST} from '../modules/fetch.js'; | ||||||
|  |  | ||||||
| function initRepoIssueListCheckboxes() { | function initRepoIssueListCheckboxes() { | ||||||
|   const $issueSelectAll = $('.issue-checkbox-all'); |   const $issueSelectAll = $('.issue-checkbox-all'); | ||||||
| @@ -146,13 +147,7 @@ function initPinRemoveButton() { | |||||||
|       const id = Number(el.getAttribute('data-issue-id')); |       const id = Number(el.getAttribute('data-issue-id')); | ||||||
|  |  | ||||||
|       // Send the unpin request |       // Send the unpin request | ||||||
|       const response = await fetch(el.getAttribute('data-unpin-url'), { |       const response = await DELETE(el.getAttribute('data-unpin-url')); | ||||||
|         method: 'delete', |  | ||||||
|         headers: { |  | ||||||
|           'X-Csrf-Token': window.config.csrfToken, |  | ||||||
|           'Content-Type': 'application/json', |  | ||||||
|         }, |  | ||||||
|       }); |  | ||||||
|       if (response.ok) { |       if (response.ok) { | ||||||
|         // Delete the tooltip |         // Delete the tooltip | ||||||
|         el._tippy.destroy(); |         el._tippy.destroy(); | ||||||
| @@ -166,14 +161,7 @@ function initPinRemoveButton() { | |||||||
| async function pinMoveEnd(e) { | async function pinMoveEnd(e) { | ||||||
|   const url = e.item.getAttribute('data-move-url'); |   const url = e.item.getAttribute('data-move-url'); | ||||||
|   const id = Number(e.item.getAttribute('data-issue-id')); |   const id = Number(e.item.getAttribute('data-issue-id')); | ||||||
|   await fetch(url, { |   await POST(url, {data: {id, position: e.newIndex + 1}}); | ||||||
|     method: 'post', |  | ||||||
|     body: JSON.stringify({id, position: e.newIndex + 1}), |  | ||||||
|     headers: { |  | ||||||
|       'X-Csrf-Token': window.config.csrfToken, |  | ||||||
|       'Content-Type': 'application/json', |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| async function initIssuePinSort() { | async function initIssuePinSort() { | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {hideElem, showElem} from '../utils/dom.js'; | 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() { | export function initRepoMigrationStatusChecker() { | ||||||
|   const $repoMigrating = $('#repo_migrating'); |   const $repoMigrating = $('#repo_migrating'); | ||||||
| @@ -13,7 +14,7 @@ export function initRepoMigrationStatusChecker() { | |||||||
|  |  | ||||||
|   // returns true if the refresh still need to be called after a while |   // returns true if the refresh still need to be called after a while | ||||||
|   const refresh = async () => { |   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 |     if (res.status !== 200) return true; // continue to refresh if network error occurs | ||||||
|  |  | ||||||
|     const data = await res.json(); |     const data = await res.json(); | ||||||
| @@ -58,12 +59,6 @@ export function initRepoMigrationStatusChecker() { | |||||||
| } | } | ||||||
|  |  | ||||||
| async function doMigrationRetry(e) { | async function doMigrationRetry(e) { | ||||||
|   await fetch($(e.target).attr('data-migrating-task-retry-url'), { |   await POST($(e.target).attr('data-migrating-task-retry-url')); | ||||||
|     method: 'post', |  | ||||||
|     headers: { |  | ||||||
|       'X-Csrf-Token': csrfToken, |  | ||||||
|       'Content-Type': 'application/json', |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
|   window.location.reload(); |   window.location.reload(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.js'; | import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.js'; | ||||||
| import {showElem} from '../utils/dom.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() { | export async function initUserAuthWebAuthn() { | ||||||
|   const elPrompt = document.querySelector('.user.signin.webauthn-prompt'); |   const elPrompt = document.querySelector('.user.signin.webauthn-prompt'); | ||||||
| @@ -13,7 +14,7 @@ export async function initUserAuthWebAuthn() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const res = await fetch(`${appSubUrl}/user/webauthn/assertion`); |   const res = await GET(`${appSubUrl}/user/webauthn/assertion`); | ||||||
|   if (res.status !== 200) { |   if (res.status !== 200) { | ||||||
|     webAuthnError('unknown'); |     webAuthnError('unknown'); | ||||||
|     return; |     return; | ||||||
| @@ -53,12 +54,8 @@ async function verifyAssertion(assertedCredential) { | |||||||
|   const sig = new Uint8Array(assertedCredential.response.signature); |   const sig = new Uint8Array(assertedCredential.response.signature); | ||||||
|   const userHandle = new Uint8Array(assertedCredential.response.userHandle); |   const userHandle = new Uint8Array(assertedCredential.response.userHandle); | ||||||
|  |  | ||||||
|   const res = await fetch(`${appSubUrl}/user/webauthn/assertion`, { |   const res = await POST(`${appSubUrl}/user/webauthn/assertion`, { | ||||||
|     method: 'POST', |     data: { | ||||||
|     headers: { |  | ||||||
|       'Content-Type': 'application/json; charset=utf-8' |  | ||||||
|     }, |  | ||||||
|     body: JSON.stringify({ |  | ||||||
|       id: assertedCredential.id, |       id: assertedCredential.id, | ||||||
|       rawId: encodeURLEncodedBase64(rawId), |       rawId: encodeURLEncodedBase64(rawId), | ||||||
|       type: assertedCredential.type, |       type: assertedCredential.type, | ||||||
| @@ -69,7 +66,7 @@ async function verifyAssertion(assertedCredential) { | |||||||
|         signature: encodeURLEncodedBase64(sig), |         signature: encodeURLEncodedBase64(sig), | ||||||
|         userHandle: encodeURLEncodedBase64(userHandle), |         userHandle: encodeURLEncodedBase64(userHandle), | ||||||
|       }, |       }, | ||||||
|     }), |     }, | ||||||
|   }); |   }); | ||||||
|   if (res.status === 500) { |   if (res.status === 500) { | ||||||
|     webAuthnError('unknown'); |     webAuthnError('unknown'); | ||||||
| @@ -88,13 +85,8 @@ async function webauthnRegistered(newCredential) { | |||||||
|   const clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); |   const clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); | ||||||
|   const rawId = new Uint8Array(newCredential.rawId); |   const rawId = new Uint8Array(newCredential.rawId); | ||||||
|  |  | ||||||
|   const res = await fetch(`${appSubUrl}/user/settings/security/webauthn/register`, { |   const res = await POST(`${appSubUrl}/user/settings/security/webauthn/register`, { | ||||||
|     method: 'POST', |     data: { | ||||||
|     headers: { |  | ||||||
|       'X-Csrf-Token': csrfToken, |  | ||||||
|       'Content-Type': 'application/json; charset=utf-8', |  | ||||||
|     }, |  | ||||||
|     body: JSON.stringify({ |  | ||||||
|       id: newCredential.id, |       id: newCredential.id, | ||||||
|       rawId: encodeURLEncodedBase64(rawId), |       rawId: encodeURLEncodedBase64(rawId), | ||||||
|       type: newCredential.type, |       type: newCredential.type, | ||||||
| @@ -102,7 +94,7 @@ async function webauthnRegistered(newCredential) { | |||||||
|         attestationObject: encodeURLEncodedBase64(attestationObject), |         attestationObject: encodeURLEncodedBase64(attestationObject), | ||||||
|         clientDataJSON: encodeURLEncodedBase64(clientDataJSON), |         clientDataJSON: encodeURLEncodedBase64(clientDataJSON), | ||||||
|       }, |       }, | ||||||
|     }), |     }, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   if (res.status === 409) { |   if (res.status === 409) { | ||||||
| @@ -165,15 +157,11 @@ export function initUserAuthWebAuthnRegister() { | |||||||
| async function webAuthnRegisterRequest() { | async function webAuthnRegisterRequest() { | ||||||
|   const elNickname = document.getElementById('nickname'); |   const elNickname = document.getElementById('nickname'); | ||||||
|  |  | ||||||
|   const body = new FormData(); |   const formData = new FormData(); | ||||||
|   body.append('name', elNickname.value); |   formData.append('name', elNickname.value); | ||||||
|  |  | ||||||
|   const res = await fetch(`${appSubUrl}/user/settings/security/webauthn/request_register`, { |   const res = await POST(`${appSubUrl}/user/settings/security/webauthn/request_register`, { | ||||||
|     method: 'POST', |     data: formData, | ||||||
|     headers: { |  | ||||||
|       'X-Csrf-Token': csrfToken, |  | ||||||
|     }, |  | ||||||
|     body, |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   if (res.status === 409) { |   if (res.status === 409) { | ||||||
|   | |||||||
| @@ -2,17 +2,18 @@ import {isObject} from '../utils.js'; | |||||||
|  |  | ||||||
| const {csrfToken} = window.config; | const {csrfToken} = window.config; | ||||||
|  |  | ||||||
|  | // safe HTTP methods that don't need a csrf token | ||||||
|  | const safeMethods = new Set(['GET', 'HEAD', 'OPTIONS', 'TRACE']); | ||||||
|  |  | ||||||
| // fetch wrapper, use below method name functions and the `data` option to pass in data | // 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, | // which will automatically set an appropriate headers. For json content, only object | ||||||
| // only object and array types are currently supported. | // and array types are currently supported. | ||||||
| function request(url, {headers, data, body, ...other} = {}) { | export function request(url, {method = 'GET', headers = {}, data, body, ...other} = {}) { | ||||||
|   let contentType; |   let contentType; | ||||||
|   if (!body) { |   if (!body) { | ||||||
|     if (data instanceof FormData) { |     if (data instanceof FormData) { | ||||||
|       contentType = 'multipart/form-data'; |  | ||||||
|       body = data; |       body = data; | ||||||
|     } else if (data instanceof URLSearchParams) { |     } else if (data instanceof URLSearchParams) { | ||||||
|       contentType = 'application/x-www-form-urlencoded'; |  | ||||||
|       body = data; |       body = data; | ||||||
|     } else if (isObject(data) || Array.isArray(data)) { |     } else if (isObject(data) || Array.isArray(data)) { | ||||||
|       contentType = 'application/json'; |       contentType = 'application/json'; | ||||||
| @@ -20,12 +21,18 @@ function request(url, {headers, data, body, ...other} = {}) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return fetch(url, { |   const headersMerged = new Headers({ | ||||||
|     headers: { |     ...(!safeMethods.has(method.toUpperCase()) && {'x-csrf-token': csrfToken}), | ||||||
|       'x-csrf-token': csrfToken, |  | ||||||
|     ...(contentType && {'content-type': contentType}), |     ...(contentType && {'content-type': contentType}), | ||||||
|       ...headers, |   }); | ||||||
|     }, |  | ||||||
|  |   for (const [name, value] of Object.entries(headers)) { | ||||||
|  |     headersMerged.set(name, value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return fetch(url, { | ||||||
|  |     method, | ||||||
|  |     headers: headersMerged, | ||||||
|     ...(body && {body}), |     ...(body && {body}), | ||||||
|     ...other, |     ...other, | ||||||
|   }); |   }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user