PluginProbe
Code Snippets / 3.10.2
Code Snippets v3.10.2
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / utils / restAPI.ts

restAPI.ts in Code Snippets 3.10.2, at js/utils/restAPI.ts

95 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { trimTrailingChar } from './text'
2 import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'
3
4 const normalizeUrl = (url: string | undefined) =>
5 trimTrailingChar(url ?? '', '/')
6
7 export const REST_BASES = {
8 snippets: normalizeUrl(window.CODE_SNIPPETS?.restAPI.snippets),
9 recentlyActive: normalizeUrl(window.CODE_SNIPPETS?.restAPI.recentlyActive),
10 preferences: normalizeUrl(window.CODE_SNIPPETS?.restAPI.preferences),
11 importPlugins: normalizeUrl(window.CODE_SNIPPETS?.restAPI.importPlugins),
12 importFiles: normalizeUrl(window.CODE_SNIPPETS?.restAPI.importFiles),
13 cloud: {
14 snippets: normalizeUrl(window.CODE_SNIPPETS?.restAPI.cloud.snippets),
15 }
16 }
17
18 /** Verbs that hosts and firewalls commonly reject outright. */
19 const OVERRIDDEN_METHODS = ['delete', 'put', 'patch']
20
21 /**
22 * Send write requests as POST, naming the intended verb in a header.
23 *
24 * Plenty of hosts allow only GET and POST, so a DELETE never reaches
25 * WordPress: the request is rejected upstream, and the browser reports a 403 —
26 * or a severed connection — that no amount of correct authentication can fix.
27 * The REST server reads `X-HTTP-Method-Override` on a POST and dispatches the
28 * route exactly as it would have, so this changes nothing WordPress sees while
29 * letting the request through.
30 */
31 export const applyMethodOverride = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
32 const method = config.method?.toLowerCase()
33
34 if (!method || !OVERRIDDEN_METHODS.includes(method)) {
35 return config
36 }
37
38 config.headers.set('X-HTTP-Method-Override', method.toUpperCase())
39 config.method = 'post'
40
41 return config
42 }
43
44 /**
45 * The REST nonce to authenticate the next request with.
46 *
47 * Held in a variable rather than baked into the axios config, because the value
48 * the page was rendered with does not stay valid. A nonce expires with the
49 * session, and the snippet editor is a screen people leave open for a long
50 * time. Once it lapsed, every save failed with a 403 and the only cure was
51 * reloading the page, which loses whatever was being written.
52 */
53 let restNonce = window.CODE_SNIPPETS?.restAPI.nonce
54
55 /**
56 * Keep the REST nonce current for as long as the page is open.
57 *
58 * WordPress already sends a freshly minted nonce with every Heartbeat response,
59 * from `wp_refresh_heartbeat_nonces()`. Core applies it to `wpApiSettings`,
60 * which our screens do not enqueue, so the value went unused. Listening for the
61 * tick ourselves means an editor left open stays able to save.
62 */
63 export const listenForNonceRefresh = () => {
64 // Heartbeat also fires the tick through the hooks API, which avoids
65 // depending on jQuery being present and typed.
66 window.wp.hooks?.addAction(
67 'heartbeat.tick',
68 'code-snippets/refresh-rest-nonce',
69 (data: { rest_nonce?: string }) => {
70 if (data.rest_nonce) {
71 restNonce = data.rest_nonce
72 }
73 }
74 )
75 }
76
77 /**
78 * Attach the current nonce to an outgoing request.
79 *
80 * Read per request, so that a nonce refreshed since page load is actually used.
81 */
82 export const applyRestNonce = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
83 if (restNonce) {
84 config.headers.set('X-WP-Nonce', restNonce)
85 }
86
87 return config
88 }
89
90 export const REST_API_AXIOS_CONFIG: AxiosRequestConfig = {
91 headers: {
92 'Access-Control': window.CODE_SNIPPETS?.restAPI.cloud.token
93 }
94 }
95