refactor(frontend): replace axios with the native Fetch API

Drop the axios (and qs) dependencies in favor of a native fetch wrapper.
axios only ever handled same-origin JSON/form calls, a CSRF header, a 401
redirect, and a 403-retry, all of which the platform now provides directly.

- New src/api/http-init.ts (replaces axios-init.ts) reimplements the
  request/response interceptors on fetch: base-path prefixing,
  X-Requested-With, same-origin credentials, the CSRF token on unsafe
  methods, a single 403 retry with token refresh, and the 401
  redirect-and-latch. A small encodeForm() reproduces qs's
  arrayFormat:'repeat' encoding, so the request wire format is unchanged.
- HttpUtil (src/utils/index.ts) keeps its public signatures and the Msg
  envelope, so the ~49 API call sites are untouched. HttpOptions is now
  hand-rolled instead of extending AxiosRequestConfig.
- PanelUpdateModal drops its lone direct axios.get in favor of HttpUtil.get
  with { silent, timeout }.
- Add tests for the fetch core (CSRF header, form/JSON/FormData bodies,
  base-path prefix, 403 retry, 401 redirect, tolerant body parse) and for
  HttpUtil's envelope unwrap / toast / error mapping; this logic was
  previously untested.
- Remove the vendor-axios manualChunks branch and the qs type shim, and
  reword stale "axios" mentions in docs and route comments.
This commit is contained in:
MHSanaei
2026-07-08 01:09:18 +02:00
parent 15faec6258
commit bc309ed9f8
21 changed files with 521 additions and 203 deletions
+1 -1
View File
@@ -1392,7 +1392,7 @@ export const sections: readonly Section[] = [
{
method: 'POST',
path: '/panel/api/xray/outbound-subs/:id/del',
summary: 'Delete an outbound subscription by id (POST alias of DELETE for axios-friendly clients).',
summary: 'Delete an outbound subscription by id (POST alias of DELETE for clients that cannot send DELETE).',
params: [
{ name: 'id', in: 'path', type: 'integer', desc: 'Subscription id.' },
],
@@ -2,7 +2,6 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Button, Modal, Switch, Tag } from 'antd';
import { CloudDownloadOutlined } from '@ant-design/icons';
import axios from 'axios';
import { HttpUtil, PromiseUtil } from '@/utils';
import { formatPanelVersion } from '@/lib/panel-version';
@@ -53,8 +52,12 @@ export default function PanelUpdateModal({
const deadline = Date.now() + 90_000;
while (Date.now() < deadline) {
try {
const r = await axios.get('/panel/api/server/getUpdateStatus', { timeout: 2000 });
const status = r?.data?.obj as PanelUpdateStatus | undefined;
const msg = await HttpUtil.get<PanelUpdateStatus>(
'/panel/api/server/getUpdateStatus',
undefined,
{ silent: true, timeout: 2000 },
);
const status = msg?.obj ?? undefined;
if (status?.runId === expectedRunId) {
if (status.state === 'success') return 'success';
if (status.state === 'failed') return 'failed';