Files
flvx/vite-frontend/src/api/index.ts
T
sagit 9a85363e44 feat: Announcement Popup Notification (#411)
* docs: add announcement popup design spec

* docs: add announcement popup implementation plan

* feat(api): include update_time in announcement response

* feat(ui): add update_time to AnnouncementData interface

* feat(ui): create AnnouncementModal component

* feat(ui): manage announcement modal state in dashboard hook

* feat(ui): add announcement modal to dashboard layout
2026-04-04 13:00:11 +08:00

533 lines
18 KiB
TypeScript

import type {
BatchOperationResult,
ForwardDiagnosisApiData,
ForwardApiItem,
GroupPermissionApiItem,
NodeReleaseApiItem,
NodeApiItem,
SpeedLimitApiItem,
TunnelBatchDeletePreviewApiData,
TunnelBatchDeleteWithForwardsApiData,
TunnelDeletePreviewApiData,
TunnelDeleteWithForwardsApiData,
TunnelDiagnosisApiData,
TunnelGroupApiItem,
UserApiItem,
UserGroupApiItem,
UserListQuery,
UserPackageInfoApiData,
UserTunnelPermissionApiItem,
TunnelApiItem,
UserTunnelApiItem,
UserMutationPayload,
NodeMutationPayload,
TunnelMutationPayload,
UserQuotaResetPayload,
UserTunnelAssignPayload,
UserTunnelListQuery,
UserTunnelRemovePayload,
ForwardMutationPayload,
SpeedLimitMutationPayload,
UpdatePasswordPayload,
BackupImportPayload,
NodeMetricApiItem,
TunnelMetricApiItem,
ServiceMonitorApiItem,
ServiceMonitorResultApiItem,
ServiceMonitorLimitsApiData,
ServiceMonitorMutationPayload,
MonitorNodeApiItem,
MonitorTunnelApiItem,
MonitorPermissionApiItem,
MonitorAccessApiData,
TunnelQualityApiItem,
} from "./types";
import axios from "axios";
import Network from "./network";
export type ReleaseChannel = "stable" | "dev";
// 登陆相关接口
export interface LoginData {
username: string;
password: string;
captchaId: string;
}
export interface LoginResponse {
token: string;
role_id: number;
name: string;
requirePasswordChange?: boolean;
}
export const login = (data: LoginData) =>
Network.post<LoginResponse>("/user/login", data);
// 用户CRUD操作 - 全部使用POST请求
export const createUser = (data: UserMutationPayload) =>
Network.post("/user/create", data);
export const getAllUsers = (pageData: UserListQuery = {}) =>
Network.post<UserApiItem[]>("/user/list", pageData);
export const updateUser = (data: UserMutationPayload) =>
Network.post("/user/update", data);
export const deleteUser = (id: number) => Network.post("/user/delete", { id });
export const getUserPackageInfo = () =>
Network.post<UserPackageInfoApiData>("/user/package");
// 节点CRUD操作 - 全部使用POST请求
export const createNode = (data: NodeMutationPayload) =>
Network.post("/node/create", data);
export const getNodeList = () => Network.post<NodeApiItem[]>("/node/list");
export const getDashboardNodeExpiryList = () =>
Network.post<NodeApiItem[]>("/node/list", {});
export const updateNode = (data: NodeMutationPayload) =>
Network.post("/node/update", data);
export const deleteNode = (id: number) => Network.post("/node/delete", { id });
export const getNodeInstallCommand = (
id: number,
channel: ReleaseChannel = "stable",
) => Network.post<string>("/node/install", { id, channel });
export const updateNodeOrder = (data: {
nodes: Array<{ id: number; inx: number }>;
}) => Network.post("/node/update-order", data);
export const dismissNodeExpiryReminder = (id: number) =>
Network.post("/node/dismiss-expiry-reminder", { id });
export const checkNodeStatus = (nodeId?: number) => {
const params = nodeId ? { nodeId } : {};
return Network.post("/node/check-status", params);
};
export const upgradeNode = (
id: number,
version?: string,
channel: ReleaseChannel = "stable",
) =>
Network.post(
"/node/upgrade",
{ id, version: version || "", channel },
{ timeout: 5 * 60 * 1000 },
);
export const batchUpgradeNodes = (
ids: number[],
version?: string,
channel: ReleaseChannel = "stable",
) =>
Network.post(
"/node/batch-upgrade",
{ ids, version: version || "", channel },
{ timeout: 15 * 60 * 1000 },
);
export const getNodeReleases = (channel: ReleaseChannel = "stable") =>
Network.post<NodeReleaseApiItem[]>("/node/releases", { channel });
export const rollbackNode = (id: number) =>
Network.post("/node/rollback", { id });
// 隧道CRUD操作 - 全部使用POST请求
export const createTunnel = (data: TunnelMutationPayload) =>
Network.post("/tunnel/create", data);
export const getTunnelList = () =>
Network.post<TunnelApiItem[]>("/tunnel/list");
export const getTunnelById = (id: number) =>
Network.post<TunnelApiItem>("/tunnel/get", { id });
export const updateTunnel = (data: TunnelMutationPayload) =>
Network.post("/tunnel/update", data, { timeout: 120_000 });
export const deleteTunnel = (id: number) =>
Network.post("/tunnel/delete", { id });
export const previewTunnelDelete = (id: number) =>
Network.post<TunnelDeletePreviewApiData>("/tunnel/delete-preview", { id });
export const deleteTunnelWithForwards = (data: {
id: number;
action: "replace" | "delete_forwards";
targetTunnelId?: number;
}) =>
Network.post<TunnelDeleteWithForwardsApiData | BatchOperationResult>(
"/tunnel/delete-with-forwards",
data,
);
export const previewBatchTunnelDelete = (ids: number[]) =>
Network.post<TunnelBatchDeletePreviewApiData>("/tunnel/batch-delete-preview", {
ids,
});
export const batchDeleteTunnelsWithForwards = (data: {
ids: number[];
action: "replace" | "delete_forwards";
targetTunnelId?: number;
}) =>
Network.post<TunnelBatchDeleteWithForwardsApiData>(
"/tunnel/batch-delete-with-forwards",
data,
);
export const diagnoseTunnel = (tunnelId: number) =>
Network.post<TunnelDiagnosisApiData>(
"/tunnel/diagnose",
{ tunnelId },
{ timeout: 120 * 1000 },
);
export const updateTunnelOrder = (data: {
tunnels: Array<{ id: number; inx: number }>;
}) => Network.post("/tunnel/update-order", data);
// 用户隧道权限管理操作 - 全部使用POST请求
export const assignUserTunnel = (data: UserTunnelAssignPayload) =>
Network.post("/tunnel/user/assign", data);
export const batchAssignUserTunnel = (data: {
userId: number;
tunnels: Array<{ tunnelId: number; speedId?: number | null }>;
}) => Network.post("/tunnel/user/batch-assign", data);
export const getUserTunnelList = (queryData: UserTunnelListQuery = {}) =>
Network.post<UserTunnelPermissionApiItem[]>("/tunnel/user/list", queryData);
export const removeUserTunnel = (params: UserTunnelRemovePayload) =>
Network.post("/tunnel/user/remove", params);
export const updateUserTunnel = (data: UserTunnelAssignPayload) =>
Network.post("/tunnel/user/update", data);
export const userTunnel = () =>
Network.post<UserTunnelApiItem[]>("/tunnel/user/tunnel");
// 转发CRUD操作 - 全部使用POST请求
export const createForward = (data: ForwardMutationPayload) =>
Network.post("/forward/create", data);
export const getForwardList = () =>
Network.post<ForwardApiItem[]>("/forward/list");
export const updateForward = (data: ForwardMutationPayload) =>
Network.post("/forward/update", data);
export const deleteForward = (id: number) =>
Network.post("/forward/delete", { id });
export const forceDeleteForward = (id: number) =>
Network.post("/forward/force-delete", { id });
// 转发服务控制操作 - 通过Java后端接口
export const pauseForwardService = (forwardId: number) =>
Network.post("/forward/pause", { id: forwardId });
export const resumeForwardService = (forwardId: number) =>
Network.post("/forward/resume", { id: forwardId });
// 转发诊断操作
export const diagnoseForward = (forwardId: number) =>
Network.post<ForwardDiagnosisApiData>(
"/forward/diagnose",
{ forwardId },
{ timeout: 120 * 1000 },
);
// 转发排序操作
export const updateForwardOrder = (data: {
forwards: Array<{ id: number; inx: number }>;
}) => Network.post("/forward/update-order", data);
// 限速规则CRUD操作 - 全部使用POST请求
export const createSpeedLimit = (data: SpeedLimitMutationPayload) =>
Network.post("/speed-limit/create", data);
export const getSpeedLimitList = () =>
Network.post<SpeedLimitApiItem[]>("/speed-limit/list");
export const updateSpeedLimit = (data: SpeedLimitMutationPayload) =>
Network.post("/speed-limit/update", data);
export const deleteSpeedLimit = (id: number) =>
Network.post("/speed-limit/delete", { id });
// 修改密码接口
export const updatePassword = (data: UpdatePasswordPayload) =>
Network.post("/user/updatePassword", data);
// 重置流量接口
export const resetUserFlow = (data: { id: number; type: number }) =>
Network.post("/user/reset", data);
export const resetUserQuota = (data: UserQuotaResetPayload) =>
Network.post("/user/quota/reset", data);
export const getUserGroups = (id: number) =>
Network.post<number[]>("/user/groups", { id });
// 网站配置相关接口
export const getConfigs = () =>
Network.post<Record<string, string>>("/config/list");
export const getConfigByName = (name: string) =>
Network.post<{ name: string; value: string }>("/config/get", { name });
export const updateConfigs = (configMap: Record<string, string>) =>
Network.post("/config/update", configMap);
export const updateConfig = (name: string, value: string) =>
Network.post("/config/update-single", { name, value });
export const activateLicense = (licenseKey: string) =>
Network.post("/license/activate", { license_key: licenseKey });
export const exportBackupData = () => Network.post("/backup/export");
export const importBackupData = (data: BackupImportPayload) =>
Network.post("/backup/import", data);
export const restoreBackupData = (data: BackupImportPayload) =>
Network.post("/backup/restore", data);
// 验证码相关接口
export const checkCaptcha = () => Network.post("/captcha/check");
export const generateCaptcha = () => Network.post(`/captcha/generate`);
export const verifyCaptcha = (data: { captchaId: string; trackData: string }) =>
Network.post("/captcha/verify", data);
// 批量操作接口
export const batchDeleteForwards = (ids: number[]) =>
Network.post<BatchOperationResult>("/forward/batch-delete", { ids });
export const batchPauseForwards = (ids: number[]) =>
Network.post<BatchOperationResult>("/forward/batch-pause", { ids });
export const batchResumeForwards = (ids: number[]) =>
Network.post<BatchOperationResult>("/forward/batch-resume", { ids });
export const batchDeleteTunnels = (ids: number[]) =>
Network.post<BatchOperationResult>("/tunnel/batch-delete", { ids });
export const batchDeleteNodes = (ids: number[]) =>
Network.post<BatchOperationResult>("/node/batch-delete", { ids });
export const batchRedeployForwards = (ids: number[]) =>
Network.post<BatchOperationResult>("/forward/batch-redeploy", { ids });
export const batchRedeployTunnels = (ids: number[]) =>
Network.post<BatchOperationResult>("/tunnel/batch-redeploy", { ids });
export const batchChangeTunnel = (data: {
forwardIds: number[];
targetTunnelId: number;
}) => Network.post<BatchOperationResult>("/forward/batch-change-tunnel", data);
// 分组与权限分配接口
export const getTunnelGroupList = () =>
Network.post<TunnelGroupApiItem[]>("/group/tunnel/list");
export const createTunnelGroup = (data: { name: string; status?: number }) =>
Network.post("/group/tunnel/create", data);
export const updateTunnelGroup = (data: {
id: number;
name: string;
status?: number;
}) => Network.post("/group/tunnel/update", data);
export const deleteTunnelGroup = (id: number) =>
Network.post("/group/tunnel/delete", { id });
export const assignTunnelsToGroup = (data: {
groupId: number;
tunnelIds: number[];
}) => Network.post("/group/tunnel/assign", data);
export const getUserGroupList = () =>
Network.post<UserGroupApiItem[]>("/group/user/list");
export const createUserGroup = (data: { name: string; status?: number }) =>
Network.post("/group/user/create", data);
export const updateUserGroup = (data: {
id: number;
name: string;
status?: number;
}) => Network.post("/group/user/update", data);
export const deleteUserGroup = (id: number) =>
Network.post("/group/user/delete", { id });
export const assignUsersToGroup = (data: {
groupId: number;
userIds: number[];
}) => Network.post("/group/user/assign", data);
export const getGroupPermissionList = () =>
Network.post<GroupPermissionApiItem[]>("/group/permission/list");
export const assignGroupPermission = (data: {
userGroupId: number;
tunnelGroupId: number;
}) => Network.post("/group/permission/assign", data);
export const removeGroupPermission = (id: number) =>
Network.post("/group/permission/remove", { id });
// 面板共享 (Federation) 接口
export const getPeerShareList = () =>
Network.post<Array<Record<string, unknown>>>("/federation/share/list");
export const createPeerShare = (data: {
name: string;
nodeId: number;
maxBandwidth?: number;
expiryTime?: number;
portRangeStart?: number;
portRangeEnd?: number;
allowedDomains?: string;
allowedIps?: string;
}) => Network.post("/federation/share/create", data);
export const updatePeerShare = (data: {
id: number;
name: string;
maxBandwidth: number;
expiryTime: number;
portRangeStart: number;
portRangeEnd: number;
allowedDomains: string;
allowedIps: string;
}) => Network.post("/federation/share/update", data);
export const deletePeerShare = (id: number) =>
Network.post("/federation/share/delete", { id });
export const resetPeerShareFlow = (id: number) =>
Network.post("/federation/share/reset-flow", { id });
export const getPeerRemoteUsageList = () =>
Network.post<Array<Record<string, unknown>>>(
"/federation/share/remote-usage/list",
);
export const importRemoteNode = (data: { remoteUrl: string; token: string }) =>
Network.post("/federation/node/import", data);
export interface BackupTypes {
users?: boolean;
nodes?: boolean;
tunnels?: boolean;
forwards?: boolean;
userTunnels?: boolean;
speedLimits?: boolean;
tunnelGroups?: boolean;
userGroups?: boolean;
permissions?: boolean;
configs?: boolean;
}
export const exportBackup = async (types: string[] = []) => {
const token = window.localStorage.getItem("token");
const baseURL = axios.defaults.baseURL || "/api/v1/";
const response = await axios.post(
`${baseURL}/backup/export`,
{ types },
{
headers: {
Authorization: token,
"Content-Type": "application/json",
},
responseType: "blob",
},
);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, "");
link.setAttribute("download", `backup_${timestamp}.json`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
};
export const importBackup = (data: BackupImportPayload) =>
Network.post("/backup/import", data);
export interface AnnouncementData {
content: string;
enabled: number;
update_time?: number;
}
export const getAnnouncement = () =>
Network.get<AnnouncementData>("/announcement/get");
export const updateAnnouncement = (data: AnnouncementData) =>
Network.post("/announcement/update", data);
export const getNodeMetrics = (
nodeId: number,
start?: number,
end?: number,
) => {
const params: Record<string, string> = {};
if (start) params.start = String(start);
if (end) params.end = String(end);
return Network.get<NodeMetricApiItem[]>(
`/monitor/nodes/${nodeId}/metrics`,
params,
{ timeout: 60_000 },
);
};
export const getNodeMetricsLatest = (nodeId: number) =>
Network.get<NodeMetricApiItem>(`/monitor/nodes/${nodeId}/metrics/latest`);
export const getTunnelMetrics = (
tunnelId: number,
start?: number,
end?: number,
) => {
const params: Record<string, string> = {};
if (start) params.start = String(start);
if (end) params.end = String(end);
return Network.get<TunnelMetricApiItem[]>(
`/monitor/tunnels/${tunnelId}/metrics`,
params,
);
};
export const getMonitorTunnels = () =>
Network.get<MonitorTunnelApiItem[]>("/monitor/tunnels");
export const getMonitorTunnelQuality = () =>
Network.get<TunnelQualityApiItem[]>("/monitor/tunnels/quality");
export const getMonitorTunnelQualityHistory = (
tunnelId: number,
start?: number,
end?: number,
) => {
const params: Record<string, string> = {};
if (start) params.start = String(start);
if (end) params.end = String(end);
return Network.get<TunnelQualityApiItem[]>(
`/monitor/tunnels/${tunnelId}/quality`,
params,
);
};
export const getServiceMonitorList = () =>
Network.get<ServiceMonitorApiItem[]>("/monitor/services");
export const getServiceMonitorLimits = () =>
Network.get<ServiceMonitorLimitsApiData>("/monitor/services/limits");
export const createServiceMonitor = (data: ServiceMonitorMutationPayload) =>
Network.post<ServiceMonitorApiItem>("/monitor/services/create", data);
export const updateServiceMonitor = (data: ServiceMonitorMutationPayload) =>
Network.post<ServiceMonitorApiItem>("/monitor/services/update", data);
export const deleteServiceMonitor = (id: number) =>
Network.post("/monitor/services/delete", { id });
export const getServiceMonitorResults = (
monitorId: number,
options?: { limit?: number; start?: number; end?: number },
) => {
const params: Record<string, string> = {};
if (options?.start != null && options?.end != null) {
params.start = String(options.start);
params.end = String(options.end);
} else if (options?.limit != null) {
params.limit = String(options.limit);
}
return Network.get<ServiceMonitorResultApiItem[]>(
`/monitor/services/${monitorId}/results`,
params,
);
};
export const getServiceMonitorLatestResults = () =>
Network.get<ServiceMonitorResultApiItem[]>(
"/monitor/services/latest-results",
);
export const runServiceMonitor = (id: number) =>
Network.post<ServiceMonitorResultApiItem>("/monitor/services/run", { id });
export const getMonitorNodes = () =>
Network.get<MonitorNodeApiItem[]>("/monitor/nodes");
export const getMonitorAccess = () =>
Network.get<MonitorAccessApiData>("/monitor/access");
export const getMonitorPermissionList = () =>
Network.get<MonitorPermissionApiItem[]>("/monitor/permission/list");
export const assignMonitorPermission = (userId: number) =>
Network.post("/monitor/permission/assign", { userId });
export const removeMonitorPermission = (userId: number) =>
Network.post("/monitor/permission/remove", { userId });