diff --git a/bin/compactAllPads.ts b/bin/compactAllPads.ts index ac247e8f6c5..c7c03ac1212 100644 --- a/bin/compactAllPads.ts +++ b/bin/compactAllPads.ts @@ -24,7 +24,6 @@ import path from 'node:path'; import fs from 'node:fs'; import process from 'node:process'; -import axios from 'axios'; export type CompactAllOpts = { keepRevisions: number | null; @@ -33,8 +32,8 @@ export type CompactAllOpts = { // Minimal interface mirroring the API endpoints the script needs. Tests // substitute their own implementation that goes through supertest+JWT -// instead of axios+APIKEY, so the loop logic is exercised against a real -// running server without dragging in apikey-file or axios setup. +// instead of fetch+APIKEY, so the loop logic is exercised against a real +// running server without dragging in apikey-file or fetch setup. export type CompactAllApi = { listAllPads(): Promise; getRevisionsCount(padId: string): Promise; @@ -182,8 +181,18 @@ if (isMain) { process.on('unhandledRejection', (err) => { throw err; }); const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings(); - axios.defaults.baseURL = - `${settings.ssl ? 'https' : 'http'}://${settings.ip}:${settings.port}`; + const baseURL = `${settings.ssl ? 'https' : 'http'}://${settings.ip}:${settings.port}`; + + const apiGet = async (p: string): Promise => { + const r = await fetch(baseURL + p); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); + }; + const apiPost = async (p: string): Promise => { + const r = await fetch(baseURL + p, {method: 'POST'}); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); + }; const opts = parseArgs(process.argv.slice(2)); if (!opts) usage(); @@ -191,32 +200,32 @@ if (isMain) { const apikey = fs.readFileSync( path.join(__dirname, '../APIKEY.txt'), {encoding: 'utf-8'}).trim(); - // Bind the abstract API to axios + APIKEY auth for the CLI shell. + // Bind the abstract API to fetch + APIKEY auth for the CLI shell. const cliApi: CompactAllApi = { async listAllPads() { - const apiInfo = await axios.get('/api/'); - const apiVersion: string | undefined = apiInfo.data.currentVersion; + const apiInfo = await apiGet('/api/'); + const apiVersion: string | undefined = apiInfo.currentVersion; if (!apiVersion) throw new Error('No version set in API'); // Stash on this for subsequent calls. Avoids a per-call /api/ ping. (cliApi as any)._apiVersion = apiVersion; - const r = await axios.get(`/api/${apiVersion}/listAllPads?apikey=${apikey}`); - if (r.data.code !== 0) throw new Error(JSON.stringify(r.data)); - return r.data.data.padIDs ?? []; + const r = await apiGet(`/api/${apiVersion}/listAllPads?apikey=${apikey}`); + if (r.code !== 0) throw new Error(JSON.stringify(r)); + return r.data.padIDs ?? []; }, async getRevisionsCount(padId: string) { const v = (cliApi as any)._apiVersion; - const r = await axios.get( + const r = await apiGet( `/api/${v}/getRevisionsCount?apikey=${apikey}` + `&padID=${encodeURIComponent(padId)}`); - if (r.data.code !== 0) throw new Error(JSON.stringify(r.data)); - return r.data.data.revisions; + if (r.code !== 0) throw new Error(JSON.stringify(r)); + return r.data.revisions; }, async compactPad(padId: string, keepRevisions: number | null) { const v = (cliApi as any)._apiVersion; const params = new URLSearchParams({apikey, padID: padId}); if (keepRevisions != null) params.set('keepRevisions', String(keepRevisions)); - const r = await axios.post(`/api/${v}/compactPad?${params.toString()}`); - if (r.data.code !== 0) throw new Error(JSON.stringify(r.data)); + const r = await apiPost(`/api/${v}/compactPad?${params.toString()}`); + if (r.code !== 0) throw new Error(JSON.stringify(r)); }, }; diff --git a/bin/compactPad.ts b/bin/compactPad.ts index f808669cbd8..69f521ba2c5 100644 --- a/bin/compactPad.ts +++ b/bin/compactPad.ts @@ -19,7 +19,6 @@ import path from 'node:path'; import fs from 'node:fs'; import process from 'node:process'; -import axios from 'axios'; // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. @@ -27,8 +26,18 @@ process.on('unhandledRejection', (err) => { throw err; }); const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings(); -axios.defaults.baseURL = - `${settings.ssl ? 'https' : 'http'}://${settings.ip}:${settings.port}`; +const baseURL = `${settings.ssl ? 'https' : 'http'}://${settings.ip}:${settings.port}`; + +const apiGet = async (p: string): Promise => { + const r = await fetch(baseURL + p); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); +}; +const apiPost = async (p: string): Promise => { + const r = await fetch(baseURL + p, {method: 'POST'}); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); +}; const usage = () => { console.error('Usage:'); @@ -56,33 +65,33 @@ const filePath = path.join(__dirname, '../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}).trim(); (async () => { - const apiInfo = await axios.get('/api/'); - const apiVersion: string | undefined = apiInfo.data.currentVersion; + const apiInfo = await apiGet('/api/'); + const apiVersion: string | undefined = apiInfo.currentVersion; if (!apiVersion) throw new Error('No version set in API'); // Pre-flight: show current revision count so operators can eyeball impact. const countUri = `/api/${apiVersion}/getRevisionsCount?apikey=${apikey}&padID=${padId}`; - const countRes = await axios.get(countUri); - if (countRes.data.code !== 0) { - console.error(`getRevisionsCount failed: ${JSON.stringify(countRes.data)}`); + const countRes = await apiGet(countUri); + if (countRes.code !== 0) { + console.error(`getRevisionsCount failed: ${JSON.stringify(countRes)}`); process.exit(1); } - const before: number = countRes.data.data.revisions; + const before: number = countRes.data.revisions; const strategy = keepRevisions == null ? 'collapse all' : `keep last ${keepRevisions}`; console.log(`Pad ${padId}: ${before + 1} revision(s). Strategy: ${strategy}.`); const params = new URLSearchParams({apikey, padID: padId}); if (keepRevisions != null) params.set('keepRevisions', String(keepRevisions)); - const result = await axios.post(`/api/${apiVersion}/compactPad?${params.toString()}`); - if (result.data.code !== 0) { - console.error(`compactPad failed: ${JSON.stringify(result.data)}`); + const result = await apiPost(`/api/${apiVersion}/compactPad?${params.toString()}`); + if (result.code !== 0) { + console.error(`compactPad failed: ${JSON.stringify(result)}`); process.exit(1); } // Post-flight: the pad is now compacted. Re-read the rev count so the // operator sees concrete savings. - const afterRes = await axios.get(countUri); - const after: number | undefined = afterRes.data?.data?.revisions; + const afterRes = await apiGet(countUri); + const after: number | undefined = afterRes?.data?.revisions; if (after != null) { console.log(`Done. Pad ${padId}: ${after + 1} revision(s) remaining ` + `(was ${before + 1}).`); diff --git a/bin/createUserSession.ts b/bin/createUserSession.ts index 095cebb0e7d..6b05bc85d2e 100644 --- a/bin/createUserSession.ts +++ b/bin/createUserSession.ts @@ -13,46 +13,54 @@ import path from "node:path"; import querystring from "node:querystring"; -import axios from 'axios' import process from "node:process"; process.on('unhandledRejection', (err) => { throw err; }); import settings from 'ep_etherpad-lite/node/utils/Settings'; (async () => { - axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`; - const api = axios; + const baseURL = `http://${settings.ip}:${settings.port}`; + const apiGet = async (p: string): Promise => { + const r = await fetch(baseURL + p); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); + }; + const apiPost = async (p: string): Promise => { + const r = await fetch(baseURL + p, {method: 'POST'}); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); + }; const filePath = path.join(__dirname, '../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); let res; - res = await api.get('/api/'); - const apiVersion = res.data.currentVersion; + res = await apiGet('/api/'); + const apiVersion = res.currentVersion; if (!apiVersion) throw new Error('No version set in API'); console.log('apiVersion', apiVersion); const uri = (cmd: string, args: querystring.ParsedUrlQueryInput ) => `/api/${apiVersion}/${cmd}?${querystring.stringify(args)}`; - res = await api.post(uri('createGroup', {apikey})); - if (res.data.code === 1) throw new Error(`Error creating group: ${res.data}`); - const groupID = res.data.data.groupID; + res = await apiPost(uri('createGroup', {apikey})); + if (res.code === 1) throw new Error(`Error creating group: ${res}`); + const groupID = res.data.groupID; console.log('groupID', groupID); - res = await api.post(uri('createGroupPad', {apikey, groupID})); - if (res.data.code === 1) throw new Error(`Error creating group pad: ${res.data}`); - console.log('Test Pad ID ====> ', res.data.data.padID); + res = await apiPost(uri('createGroupPad', {apikey, groupID})); + if (res.code === 1) throw new Error(`Error creating group pad: ${res}`); + console.log('Test Pad ID ====> ', res.data.padID); - res = await api.post(uri('createAuthor', {apikey})); - if (res.data.code === 1) throw new Error(`Error creating author: ${res.data}`); - const authorID = res.data.data.authorID; + res = await apiPost(uri('createAuthor', {apikey})); + if (res.code === 1) throw new Error(`Error creating author: ${res}`); + const authorID = res.data.authorID; console.log('authorID', authorID); const validUntil = Math.floor(new Date().getTime() / 1000) + 60000; console.log('validUntil', validUntil); - res = await api.post(uri('createSession', {apikey, groupID, authorID, validUntil})); - if (res.data.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res.data)}`); + res = await apiPost(uri('createSession', {apikey, groupID, authorID, validUntil})); + if (res.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res)}`); console.log('Session made: ====> create a cookie named sessionID and set the value to', - res.data.data.sessionID); + res.data.sessionID); process.exit(0) })(); diff --git a/bin/deleteAllGroupSessions.ts b/bin/deleteAllGroupSessions.ts index 6064757ec31..67b950d285d 100644 --- a/bin/deleteAllGroupSessions.ts +++ b/bin/deleteAllGroupSessions.ts @@ -11,7 +11,6 @@ import fs from "node:fs"; import process from "node:process"; process.on('unhandledRejection', (err) => { throw err; }); -import axios from 'axios' // Set a delete counter which will increment on each delete attempt // TODO: Check delete is successful before incrementing let deleteCount = 0; @@ -23,29 +22,38 @@ const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSe (async () => { const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); - axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`; - - const apiVersionResponse = await axios.get('/api/'); - const apiVersion = apiVersionResponse.data.currentVersion; // 1.12.5 + const baseURL = `http://${settings.ip}:${settings.port}`; + const apiGet = async (p: string): Promise => { + const r = await fetch(baseURL + p); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); + }; + const apiPost = async (p: string): Promise => { + const r = await fetch(baseURL + p, {method: 'POST'}); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); + }; + + const apiVersionResponse = await apiGet('/api/'); + const apiVersion = apiVersionResponse.currentVersion; // 1.12.5 console.log('apiVersion', apiVersion); - const groupsResponse = await axios.get(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`); - const groups = groupsResponse.data.data.groupIDs; // ['whateverGroupID'] + const groupsResponse = await apiGet(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`); + const groups = groupsResponse.data.groupIDs; // ['whateverGroupID'] for (const groupID of groups) { const sessionURI = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`; - const sessionsResponse = await axios.get(sessionURI); - const sessions = sessionsResponse.data.data; + const sessionsResponse = await apiGet(sessionURI); + const sessions = sessionsResponse.data; if(sessions == null) continue; for (const [sessionID, val] of Object.entries(sessions)) { if(val == null) continue; const deleteURI = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`; - await axios.post(deleteURI).then(c=>{ - console.log(c.data) - deleteCount++; - }); // delete + const c = await apiPost(deleteURI); + console.log(c); + deleteCount++; } } console.log(`Deleted ${deleteCount} sessions`); diff --git a/bin/deletePad.ts b/bin/deletePad.ts index 2f6009158a6..1261bddf9c3 100644 --- a/bin/deletePad.ts +++ b/bin/deletePad.ts @@ -11,13 +11,22 @@ import path from "node:path"; import fs from "node:fs"; import process from "node:process"; -import axios from "axios"; process.on('unhandledRejection', (err) => { throw err; }); const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSettings(); -axios.defaults.baseURL = `http://${settings.ip}:${settings.port}`; +const baseURL = `http://${settings.ip}:${settings.port}`; +const apiGet = async (p: string): Promise => { + const r = await fetch(baseURL + p); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); +}; +const apiPost = async (p: string): Promise => { + const r = await fetch(baseURL + p, {method: 'POST'}); + if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`); + return r.json(); +}; if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID'); @@ -29,14 +38,14 @@ const filePath = path.join(__dirname, '../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); (async () => { - let apiVersion = await axios.get('/api/'); - apiVersion = apiVersion.data.currentVersion; + const apiInfo = await apiGet('/api/'); + const apiVersion = apiInfo.currentVersion; if (!apiVersion) throw new Error('No version set in API'); // Now we know the latest API version, let's delete pad const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`; - const deleteAttempt = await axios.post(uri); - if (deleteAttempt.data.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.data}`); - console.log('Deleted pad', deleteAttempt.data); + const deleteAttempt = await apiPost(uri); + if (deleteAttempt.code === 1) throw new Error(`Error deleting pad ${deleteAttempt}`); + console.log('Deleted pad', deleteAttempt); process.exit(0) })(); diff --git a/bin/package.json b/bin/package.json index 2ede56469fe..5dd922ae357 100644 --- a/bin/package.json +++ b/bin/package.json @@ -7,7 +7,6 @@ "doc": "doc" }, "dependencies": { - "axios": "^1.16.0", "ep_etherpad-lite": "workspace:../src", "log4js": "^6.9.1", "semver": "^7.7.4", diff --git a/bin/plugins/stalePlugins.ts b/bin/plugins/stalePlugins.ts index 563bf51d126..4ca6421408d 100644 --- a/bin/plugins/stalePlugins.ts +++ b/bin/plugins/stalePlugins.ts @@ -2,22 +2,20 @@ // Returns a list of stale plugins and their authors email -import axios from 'axios' import process from "node:process"; const currentTime = new Date(); (async () => { - const res = await axios.get('https://static.etherpad.org/plugins.full.json'); - for (const plugin of Object.keys(res.data)) { - // @ts-ignore - const name = res.data[plugin].data.name; - // @ts-ignore - const date = new Date(res.data[plugin].time); + const resp = await fetch('https://static.etherpad.org/plugins.full.json'); + if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`); + const data: any = await resp.json(); + for (const plugin of Object.keys(data)) { + const name = data[plugin].data.name; + const date = new Date(data[plugin].time); const diffTime = Math.abs(currentTime.getTime() - date.getTime()); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays > (365 * 2)) { - // @ts-ignore - console.log(`${name}, ${res.data[plugin].data.maintainers[0].email}`); + console.log(`${name}, ${data[plugin].data.maintainers[0].email}`); } } process.exit(0) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06e73aca298..ae526f738ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,9 +122,6 @@ importers: bin: dependencies: - axios: - specifier: ^1.16.0 - version: 1.16.0 ep_etherpad-lite: specifier: workspace:../src version: link:../src @@ -162,7 +159,7 @@ importers: version: 0.129.0 vitepress: specifier: ^2.0.0-alpha.17 - version: 2.0.0-alpha.17(@types/node@25.6.0)(axios@1.16.0)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3) + version: 2.0.0-alpha.17(@types/node@25.6.0)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3) src: dependencies: @@ -172,9 +169,6 @@ importers: async: specifier: ^3.2.6 version: 3.2.6 - axios: - specifier: ^1.16.0 - version: 1.16.0 cassandra-driver: specifier: ^4.8.0 version: 4.8.0 @@ -331,6 +325,9 @@ importers: underscore: specifier: 1.13.8 version: 1.13.8 + undici: + specifier: ^7.16.0 + version: 7.25.0 unorm: specifier: 1.6.0 version: 1.6.0 @@ -2643,9 +2640,6 @@ packages: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} - axios@1.16.0: - resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} - babel-plugin-react-compiler@19.1.0-rc.3: resolution: {integrity: sha512-mjRn69WuTz4adL0bXGx8Rsyk1086zFJeKmes6aK0xPuK3aaXmDJdLHqwKKMrpm6KAI1MCoUK72d2VeqQbu8YIA==} @@ -3490,15 +3484,6 @@ packages: focus-trap@8.0.0: resolution: {integrity: sha512-Aa84FOGHs99vVwufDMdq2qgOwXPC2e9U66GcqBhn1/jEHPDhJaP8PYhkIbqG9lhfL5Kddk/567lj46LLHYCRUw==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -4710,10 +4695,6 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - proxy-from-env@2.1.0: - resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} - engines: {node: '>=10'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -7717,13 +7698,12 @@ snapshots: '@vueuse/shared': 14.2.1(vue@3.5.30(typescript@6.0.3)) vue: 3.5.30(typescript@6.0.3) - '@vueuse/integrations@14.2.1(axios@1.16.0)(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3))': + '@vueuse/integrations@14.2.1(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3))': dependencies: '@vueuse/core': 14.2.1(vue@3.5.30(typescript@6.0.3)) '@vueuse/shared': 14.2.1(vue@3.5.30(typescript@6.0.3)) vue: 3.5.30(typescript@6.0.3) optionalDependencies: - axios: 1.16.0 focus-trap: 8.0.0 jwt-decode: 4.0.0 @@ -7881,14 +7861,6 @@ snapshots: aws-ssl-profiles@1.1.2: {} - axios@1.16.0: - dependencies: - follow-redirects: 1.16.0 - form-data: 4.0.5 - proxy-from-env: 2.1.0 - transitivePeerDependencies: - - debug - babel-plugin-react-compiler@19.1.0-rc.3: dependencies: '@babel/types': 7.28.4 @@ -8872,8 +8844,6 @@ snapshots: dependencies: tabbable: 6.4.0 - follow-redirects@1.16.0: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -10196,8 +10166,6 @@ snapshots: proxy-from-env@1.1.0: {} - proxy-from-env@2.1.0: {} - punycode@2.3.1: {} qs@6.15.0: @@ -11240,7 +11208,7 @@ snapshots: fsevents: 2.3.3 tsx: 4.21.0 - vitepress@2.0.0-alpha.17(@types/node@25.6.0)(axios@1.16.0)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3): + vitepress@2.0.0-alpha.17(@types/node@25.6.0)(jwt-decode@4.0.0)(lightningcss@1.32.0)(oxc-minify@0.129.0)(postcss@8.5.14)(tsx@4.21.0)(typescript@6.0.3): dependencies: '@docsearch/css': 4.6.0 '@docsearch/js': 4.6.0 @@ -11254,7 +11222,7 @@ snapshots: '@vue/devtools-api': 8.1.0 '@vue/shared': 3.5.30 '@vueuse/core': 14.2.1(vue@3.5.30(typescript@6.0.3)) - '@vueuse/integrations': 14.2.1(axios@1.16.0)(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3)) + '@vueuse/integrations': 14.2.1(focus-trap@8.0.0)(jwt-decode@4.0.0)(vue@3.5.30(typescript@6.0.3)) focus-trap: 8.0.0 mark.js: 8.11.1 minisearch: 7.2.0 diff --git a/src/node/server.ts b/src/node/server.ts index 3311367461a..2e06cf6f26a 100755 --- a/src/node/server.ts +++ b/src/node/server.ts @@ -27,7 +27,7 @@ import {ErrorCaused} from "./types/ErrorCaused"; import log4js from 'log4js'; import pkg from '../package.json'; import {checkForMigration} from "../static/js/pluginfw/installer"; -import axios from "axios"; +import {ProxyAgent, setGlobalDispatcher} from 'undici'; import settings from './utils/Settings'; @@ -38,28 +38,10 @@ if (settings.dumpOnUncleanExit) { wtfnode = require('wtfnode'); } - -const addProxyToAxios = (url: URL) => { - axios.defaults.proxy = { - host: url.hostname, - auth: { - username: url.username, - password: url.password, - }, - port: Number(url.port), - protocol: url.protocol, - } -} - -if(process.env['http_proxy']) { - console.log("Using proxy: " + process.env['http_proxy']) - addProxyToAxios(new URL(process.env['http_proxy'])); -} - - -if (process.env['https_proxy']) { - console.log("Using proxy: " + process.env['https_proxy']) - addProxyToAxios(new URL(process.env['https_proxy'])); +const proxyUrl = process.env['https_proxy'] || process.env['http_proxy']; +if (proxyUrl) { + console.log("Using proxy: " + proxyUrl); + setGlobalDispatcher(new ProxyAgent(proxyUrl)); } diff --git a/src/node/utils/UpdateCheck.ts b/src/node/utils/UpdateCheck.ts index da292e373b7..611e988201d 100644 --- a/src/node/utils/UpdateCheck.ts +++ b/src/node/utils/UpdateCheck.ts @@ -1,7 +1,6 @@ 'use strict'; import semver from 'semver'; import settings, {getEpVersion} from './Settings'; -import axios from 'axios'; const headers = { 'User-Agent': 'Etherpad/' + getEpVersion(), } @@ -20,9 +19,10 @@ const loadEtherpadInformations = () => { return infos; } - return axios.get(`${settings.updateServer}/info.json`, {headers: headers}) - .then(async (resp: any) => { - infos = await resp.data; + return fetch(`${settings.updateServer}/info.json`, {headers}) + .then(async (resp) => { + if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`); + infos = await resp.json() as Infos; if (infos === undefined || infos === null) { await Promise.reject("Could not retrieve current version") return diff --git a/src/package.json b/src/package.json index 77e5ddccfb6..a50c67f9492 100644 --- a/src/package.json +++ b/src/package.json @@ -32,7 +32,6 @@ "dependencies": { "@elastic/elasticsearch": "^9.4.0", "async": "^3.2.6", - "axios": "^1.16.0", "cassandra-driver": "^4.8.0", "cookie-parser": "^1.4.7", "cross-env": "^10.1.0", @@ -85,6 +84,7 @@ "tsx": "4.21.0", "ueberdb2": "^5.0.48", "underscore": "1.13.8", + "undici": "^7.16.0", "unorm": "1.6.0", "wtfnode": "^0.10.1" }, diff --git a/src/static/js/pluginfw/installer.ts b/src/static/js/pluginfw/installer.ts index 73f4195d5d5..8f8f937e701 100644 --- a/src/static/js/pluginfw/installer.ts +++ b/src/static/js/pluginfw/installer.ts @@ -2,7 +2,6 @@ import log4js from "log4js"; -import axios, {AxiosResponse} from "axios"; import {PackageData, PackageInfo} from "../../../node/types/PackageInfo"; import {MapArrayType} from "../../../node/types/MapType"; @@ -177,8 +176,11 @@ export const getAvailablePlugins = async (maxCacheAge: number | false) => { return availablePlugins; } - const pluginsLoaded: AxiosResponse> = await axios.get(`${settings.updateServer}/plugins.json`, {headers}) - const data = pluginsLoaded.data; + const pluginsLoaded = await fetch(`${settings.updateServer}/plugins.json`, {headers}); + if (!pluginsLoaded.ok) { + throw new Error(`HTTP ${pluginsLoaded.status} ${pluginsLoaded.statusText}`); + } + const data = await pluginsLoaded.json() as MapArrayType; // Normalize: the registry may use numeric keys instead of plugin names const normalized: MapArrayType = {}; for (const key in data) { diff --git a/src/tests/backend/fuzzImportTest.ts b/src/tests/backend/fuzzImportTest.ts index 5b959bbc23a..eb513fcf1fd 100644 --- a/src/tests/backend/fuzzImportTest.ts +++ b/src/tests/backend/fuzzImportTest.ts @@ -6,7 +6,6 @@ const settings = require('../container/loadSettings').loadSettings(); const common = require('./common'); const host = `http://${settings.ip}:${settings.port}`; const froth = require('mocha-froth'); -const axios = require('axios'); const apiVersion = 1; const testPadId = `TEST_fuzz${makeid()}`; @@ -28,37 +27,34 @@ setTimeout(() => { }, 5000); // wait 5 seconds async function runTest(number: number) { - await axios - .get(`${host + endPoint('createPad')}?padID=${testPadId}`, { + try { + const createRes = await fetch(`${host + endPoint('createPad')}?padID=${testPadId}`, { headers: { Authorization: await common.generateJWTToken(), - } - }) - .then(() => { - const req = axios.post(`${host}/p/${testPadId}/import`) - .then(() => { - console.log('Success'); - let fN = '/test.txt'; - let cT = 'text/plain'; + }, + }); + if (!createRes.ok) throw new Error(`createPad HTTP ${createRes.status}`); - // To be more aggressive every other test we mess with Etherpad - // We provide a weird file name and also set a weird contentType - if (number % 2 == 0) { - fN = froth().toString(); - cT = froth().toString(); - } + let fN = '/test.txt'; + let cT = 'text/plain'; + // To be more aggressive every other test we mess with Etherpad + // We provide a weird file name and also set a weird contentType + if (number % 2 == 0) { + fN = froth().toString(); + cT = froth().toString(); + } - const form = req.form(); - form.append('file', froth().toString(), { - filename: fN, - contentType: cT, - }); - }); - }) - .catch((err:any) => { - // @ts-ignore - throw new Error('FAILURE', err); - }) + const form = new FormData(); + form.append('file', new Blob([froth().toString()], {type: cT}), fN); + const importRes = await fetch(`${host}/p/${testPadId}/import`, { + method: 'POST', + body: form, + }); + if (!importRes.ok) throw new Error(`import HTTP ${importRes.status}`); + console.log('Success'); + } catch (err: any) { + throw new Error('FAILURE', err); + } } function makeid() { diff --git a/src/tests/backend/specs/compactPad.ts b/src/tests/backend/specs/compactPad.ts index 32d9d69421d..96886d1ad23 100644 --- a/src/tests/backend/specs/compactPad.ts +++ b/src/tests/backend/specs/compactPad.ts @@ -161,9 +161,9 @@ describe(__filename, function () { // Coverage for the per-instance bulk-compaction loop in // bin/compactAllPads.ts. We test the exported `runCompactAll` against - // an in-memory CompactAllApi rather than spawning the script + axios, + // an in-memory CompactAllApi rather than spawning the script + fetch, // so we don't have to stand up an APIKEY-auth path. The CLI shell that - // wires axios+APIKEY is a thin adapter; the loop logic — error + // wires fetch+APIKEY is a thin adapter; the loop logic — error // tolerance, dry-run, keep-last, tally — is what regresses, and that // is what this exercises. describe('runCompactAll (bin/compactAllPads loop)', function () {