mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
refactor(frontend): drop eslint-disable from InboundsPage
Type all callbacks against DBInbound from @/models/dbinbound: - state setters use DBInbound | null - helpers (projectChildThroughMaster, checkFallback, findClientIndex, exportInboundLinks, etc.) take DBInbound - drop `(dbInbounds as any[])` casts; useInbounds already returns DBInbound[] - introduce ClientMatchTarget for findClientIndex's `client` param - tighten DBInbound.clientStats to ClientStats[] (default []) - single boundary cast at <InboundList onRowAction=> to bridge InboundList's narrower DBInboundRecord (cleanup belongs with InboundList)
This commit is contained in:
parent
dd0477a839
commit
3ca776f9c9
2 changed files with 42 additions and 36 deletions
|
|
@ -38,7 +38,7 @@ export type DBInboundInit = Partial<{
|
||||||
streamSettings: RawJsonField;
|
streamSettings: RawJsonField;
|
||||||
tag: string;
|
tag: string;
|
||||||
sniffing: RawJsonField;
|
sniffing: RawJsonField;
|
||||||
clientStats: ClientStats[] | string;
|
clientStats: ClientStats[];
|
||||||
nodeId: number | null;
|
nodeId: number | null;
|
||||||
fallbackParent: FallbackParentRef | null;
|
fallbackParent: FallbackParentRef | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
@ -81,7 +81,7 @@ export class DBInbound {
|
||||||
streamSettings: RawJsonField;
|
streamSettings: RawJsonField;
|
||||||
tag: string;
|
tag: string;
|
||||||
sniffing: RawJsonField;
|
sniffing: RawJsonField;
|
||||||
clientStats: ClientStats[] | string;
|
clientStats: ClientStats[];
|
||||||
nodeId: number | null;
|
nodeId: number | null;
|
||||||
fallbackParent: FallbackParentRef | null;
|
fallbackParent: FallbackParentRef | null;
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ export class DBInbound {
|
||||||
this.streamSettings = "";
|
this.streamSettings = "";
|
||||||
this.tag = "";
|
this.tag = "";
|
||||||
this.sniffing = "";
|
this.sniffing = "";
|
||||||
this.clientStats = "";
|
this.clientStats = [];
|
||||||
this.nodeId = null;
|
this.nodeId = null;
|
||||||
this.fallbackParent = null;
|
this.fallbackParent = null;
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { lazy, useCallback, useEffect, useMemo, useState } from 'react';
|
import { lazy, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
|
|
@ -21,7 +20,7 @@ import {
|
||||||
|
|
||||||
import { HttpUtil, SizeFormatter, RandomUtil } from '@/utils';
|
import { HttpUtil, SizeFormatter, RandomUtil } from '@/utils';
|
||||||
import { Inbound } from '@/models/inbound';
|
import { Inbound } from '@/models/inbound';
|
||||||
import { coerceInboundJsonField } from '@/models/dbinbound';
|
import { coerceInboundJsonField, type DBInbound } from '@/models/dbinbound';
|
||||||
import { useTheme } from '@/hooks/useTheme';
|
import { useTheme } from '@/hooks/useTheme';
|
||||||
import { useMediaQuery } from '@/hooks/useMediaQuery';
|
import { useMediaQuery } from '@/hooks/useMediaQuery';
|
||||||
import { useWebSocket } from '@/hooks/useWebSocket';
|
import { useWebSocket } from '@/hooks/useWebSocket';
|
||||||
|
|
@ -53,6 +52,12 @@ type RowAction =
|
||||||
|
|
||||||
type GeneralAction = 'import' | 'export' | 'subs' | 'resetInbounds';
|
type GeneralAction = 'import' | 'export' | 'subs' | 'resetInbounds';
|
||||||
|
|
||||||
|
interface ClientMatchTarget {
|
||||||
|
id?: string;
|
||||||
|
email?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function InboundsPage() {
|
export default function InboundsPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isDark, isUltra, antdThemeConfig } = useTheme();
|
const { isDark, isUltra, antdThemeConfig } = useTheme();
|
||||||
|
|
@ -94,7 +99,7 @@ export default function InboundsPage() {
|
||||||
[nodesList],
|
[nodesList],
|
||||||
);
|
);
|
||||||
const hasNodeAttachedInbound = useMemo(
|
const hasNodeAttachedInbound = useMemo(
|
||||||
() => (dbInbounds || []).some((ib: any) => ib?.nodeId != null),
|
() => (dbInbounds || []).some((ib) => ib?.nodeId != null),
|
||||||
[dbInbounds],
|
[dbInbounds],
|
||||||
);
|
);
|
||||||
const showNodeInfo = hasNodeAttachedInbound || hasActiveNode;
|
const showNodeInfo = hasNodeAttachedInbound || hasActiveNode;
|
||||||
|
|
@ -106,14 +111,14 @@ export default function InboundsPage() {
|
||||||
|
|
||||||
const [formOpen, setFormOpen] = useState(false);
|
const [formOpen, setFormOpen] = useState(false);
|
||||||
const [formMode, setFormMode] = useState<'add' | 'edit'>('add');
|
const [formMode, setFormMode] = useState<'add' | 'edit'>('add');
|
||||||
const [formDbInbound, setFormDbInbound] = useState<any>(null);
|
const [formDbInbound, setFormDbInbound] = useState<DBInbound | null>(null);
|
||||||
|
|
||||||
const [infoOpen, setInfoOpen] = useState(false);
|
const [infoOpen, setInfoOpen] = useState(false);
|
||||||
const [infoDbInbound, setInfoDbInbound] = useState<any>(null);
|
const [infoDbInbound, setInfoDbInbound] = useState<DBInbound | null>(null);
|
||||||
const [infoClientIndex, setInfoClientIndex] = useState(0);
|
const [infoClientIndex, setInfoClientIndex] = useState(0);
|
||||||
|
|
||||||
const [qrOpen, setQrOpen] = useState(false);
|
const [qrOpen, setQrOpen] = useState(false);
|
||||||
const [qrDbInbound, setQrDbInbound] = useState<any>(null);
|
const [qrDbInbound, setQrDbInbound] = useState<DBInbound | null>(null);
|
||||||
|
|
||||||
const [textOpen, setTextOpen] = useState(false);
|
const [textOpen, setTextOpen] = useState(false);
|
||||||
const [textTitle, setTextTitle] = useState('');
|
const [textTitle, setTextTitle] = useState('');
|
||||||
|
|
@ -128,7 +133,7 @@ export default function InboundsPage() {
|
||||||
const [promptLoading, setPromptLoading] = useState(false);
|
const [promptLoading, setPromptLoading] = useState(false);
|
||||||
const [promptHandler, setPromptHandler] = useState<((value: string) => Promise<boolean | void> | boolean | void) | null>(null);
|
const [promptHandler, setPromptHandler] = useState<((value: string) => Promise<boolean | void> | boolean | void) | null>(null);
|
||||||
|
|
||||||
const hostOverrideFor = useCallback((dbInbound: any) => {
|
const hostOverrideFor = useCallback((dbInbound: DBInbound | null) => {
|
||||||
if (!dbInbound || dbInbound.nodeId == null) return '';
|
if (!dbInbound || dbInbound.nodeId == null) return '';
|
||||||
return nodesById.get(dbInbound.nodeId)?.address || '';
|
return nodesById.get(dbInbound.nodeId)?.address || '';
|
||||||
}, [nodesById]);
|
}, [nodesById]);
|
||||||
|
|
@ -172,8 +177,8 @@ export default function InboundsPage() {
|
||||||
}
|
}
|
||||||
}, [promptHandler]);
|
}, [promptHandler]);
|
||||||
|
|
||||||
const projectChildThroughMaster = useCallback((child: any, master: any) => {
|
const projectChildThroughMaster = useCallback((child: DBInbound, master: DBInbound): DBInbound => {
|
||||||
const projected = JSON.parse(JSON.stringify(child));
|
const projected = JSON.parse(JSON.stringify(child)) as DBInbound;
|
||||||
projected.listen = master.listen;
|
projected.listen = master.listen;
|
||||||
projected.port = master.port;
|
projected.port = master.port;
|
||||||
const masterStream = master.toInbound().stream;
|
const masterStream = master.toInbound().stream;
|
||||||
|
|
@ -183,17 +188,18 @@ export default function InboundsPage() {
|
||||||
childInbound.stream.reality = masterStream.reality;
|
childInbound.stream.reality = masterStream.reality;
|
||||||
childInbound.stream.externalProxy = masterStream.externalProxy;
|
childInbound.stream.externalProxy = masterStream.externalProxy;
|
||||||
projected.streamSettings = childInbound.stream.toString();
|
projected.streamSettings = childInbound.stream.toString();
|
||||||
return new child.constructor(projected);
|
const Ctor = child.constructor as new (data: DBInbound) => DBInbound;
|
||||||
|
return new Ctor(projected);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const checkFallback = useCallback((dbInbound: any) => {
|
const checkFallback = useCallback((dbInbound: DBInbound): DBInbound => {
|
||||||
const parent = dbInbound?.fallbackParent;
|
const parent = dbInbound?.fallbackParent;
|
||||||
if (parent?.masterId) {
|
if (parent?.masterId) {
|
||||||
const master = (dbInbounds as any[]).find((ib: any) => ib.id === parent.masterId);
|
const master = dbInbounds.find((ib) => ib.id === parent.masterId);
|
||||||
if (master) return projectChildThroughMaster(dbInbound, master);
|
if (master) return projectChildThroughMaster(dbInbound, master);
|
||||||
}
|
}
|
||||||
if (!(dbInbound?.listen as string | undefined)?.startsWith?.('@')) return dbInbound;
|
if (!dbInbound?.listen?.startsWith?.('@')) return dbInbound;
|
||||||
for (const candidate of dbInbounds as any[]) {
|
for (const candidate of dbInbounds) {
|
||||||
if (candidate.id === dbInbound.id) continue;
|
if (candidate.id === dbInbound.id) continue;
|
||||||
const parsed = candidate.toInbound();
|
const parsed = candidate.toInbound();
|
||||||
if (!parsed.isTcp) continue;
|
if (!parsed.isTcp) continue;
|
||||||
|
|
@ -205,11 +211,11 @@ export default function InboundsPage() {
|
||||||
return dbInbound;
|
return dbInbound;
|
||||||
}, [dbInbounds, projectChildThroughMaster]);
|
}, [dbInbounds, projectChildThroughMaster]);
|
||||||
|
|
||||||
const findClientIndex = useCallback((dbInbound: any, client: any) => {
|
const findClientIndex = useCallback((dbInbound: DBInbound, client: ClientMatchTarget | null) => {
|
||||||
if (!client) return 0;
|
if (!client) return 0;
|
||||||
const inbound = dbInbound.toInbound();
|
const inbound = dbInbound.toInbound();
|
||||||
const clients = inbound?.clients || [];
|
const clients = (inbound?.clients || []) as ClientMatchTarget[];
|
||||||
const idx = clients.findIndex((c: any) => {
|
const idx = clients.findIndex((c) => {
|
||||||
if (!c) return false;
|
if (!c) return false;
|
||||||
switch (dbInbound.protocol) {
|
switch (dbInbound.protocol) {
|
||||||
case 'trojan':
|
case 'trojan':
|
||||||
|
|
@ -222,7 +228,7 @@ export default function InboundsPage() {
|
||||||
return idx >= 0 ? idx : 0;
|
return idx >= 0 ? idx : 0;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const exportInboundLinks = useCallback((dbInbound: any) => {
|
const exportInboundLinks = useCallback((dbInbound: DBInbound) => {
|
||||||
const projected = checkFallback(dbInbound);
|
const projected = checkFallback(dbInbound);
|
||||||
openText({
|
openText({
|
||||||
title: t('pages.inbounds.exportLinksTitle'),
|
title: t('pages.inbounds.exportLinksTitle'),
|
||||||
|
|
@ -231,13 +237,13 @@ export default function InboundsPage() {
|
||||||
});
|
});
|
||||||
}, [checkFallback, remarkModel, hostOverrideFor, openText, t]);
|
}, [checkFallback, remarkModel, hostOverrideFor, openText, t]);
|
||||||
|
|
||||||
const exportInboundClipboard = useCallback((dbInbound: any) => {
|
const exportInboundClipboard = useCallback((dbInbound: DBInbound) => {
|
||||||
openText({ title: t('pages.inbounds.inboundJsonTitle'), content: JSON.stringify(dbInbound, null, 2) });
|
openText({ title: t('pages.inbounds.inboundJsonTitle'), content: JSON.stringify(dbInbound, null, 2) });
|
||||||
}, [openText, t]);
|
}, [openText, t]);
|
||||||
|
|
||||||
const exportInboundSubs = useCallback((dbInbound: any) => {
|
const exportInboundSubs = useCallback((dbInbound: DBInbound) => {
|
||||||
const inbound = dbInbound.toInbound();
|
const inbound = dbInbound.toInbound();
|
||||||
const clients = inbound?.clients || [];
|
const clients = (inbound?.clients || []) as { subId?: string }[];
|
||||||
const subLinks: string[] = [];
|
const subLinks: string[] = [];
|
||||||
for (const c of clients) {
|
for (const c of clients) {
|
||||||
if (c.subId && subSettings.subURI) {
|
if (c.subId && subSettings.subURI) {
|
||||||
|
|
@ -253,7 +259,7 @@ export default function InboundsPage() {
|
||||||
|
|
||||||
const exportAllLinks = useCallback(async () => {
|
const exportAllLinks = useCallback(async () => {
|
||||||
const hydrated = await Promise.all(
|
const hydrated = await Promise.all(
|
||||||
(dbInbounds as any[]).map((ib) => hydrateInbound(ib.id).then((r) => r ?? ib)),
|
dbInbounds.map((ib) => hydrateInbound(ib.id).then((r) => r ?? ib)),
|
||||||
);
|
);
|
||||||
const out: string[] = [];
|
const out: string[] = [];
|
||||||
for (const ib of hydrated) {
|
for (const ib of hydrated) {
|
||||||
|
|
@ -265,12 +271,12 @@ export default function InboundsPage() {
|
||||||
|
|
||||||
const exportAllSubs = useCallback(async () => {
|
const exportAllSubs = useCallback(async () => {
|
||||||
const hydrated = await Promise.all(
|
const hydrated = await Promise.all(
|
||||||
(dbInbounds as any[]).map((ib) => hydrateInbound(ib.id).then((r) => r ?? ib)),
|
dbInbounds.map((ib) => hydrateInbound(ib.id).then((r) => r ?? ib)),
|
||||||
);
|
);
|
||||||
const out: string[] = [];
|
const out: string[] = [];
|
||||||
for (const ib of hydrated) {
|
for (const ib of hydrated) {
|
||||||
const inbound = ib.toInbound();
|
const inbound = ib.toInbound();
|
||||||
const clients = inbound?.clients || [];
|
const clients = (inbound?.clients || []) as { subId?: string }[];
|
||||||
for (const c of clients) {
|
for (const c of clients) {
|
||||||
if (c.subId && subSettings.subURI) {
|
if (c.subId && subSettings.subURI) {
|
||||||
out.push(subSettings.subURI + c.subId);
|
out.push(subSettings.subURI + c.subId);
|
||||||
|
|
@ -303,13 +309,13 @@ export default function InboundsPage() {
|
||||||
setFormOpen(true);
|
setFormOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const openEdit = useCallback((dbInbound: any) => {
|
const openEdit = useCallback((dbInbound: DBInbound) => {
|
||||||
setFormMode('edit');
|
setFormMode('edit');
|
||||||
setFormDbInbound(dbInbound);
|
setFormDbInbound(dbInbound);
|
||||||
setFormOpen(true);
|
setFormOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const confirmDelete = useCallback((dbInbound: any) => {
|
const confirmDelete = useCallback((dbInbound: DBInbound) => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: t('pages.inbounds.deleteConfirmTitle', { remark: dbInbound.remark }),
|
title: t('pages.inbounds.deleteConfirmTitle', { remark: dbInbound.remark }),
|
||||||
content: t('pages.inbounds.deleteConfirmContent'),
|
content: t('pages.inbounds.deleteConfirmContent'),
|
||||||
|
|
@ -323,7 +329,7 @@ export default function InboundsPage() {
|
||||||
});
|
});
|
||||||
}, [modal, refresh, t]);
|
}, [modal, refresh, t]);
|
||||||
|
|
||||||
const confirmResetTraffic = useCallback((dbInbound: any) => {
|
const confirmResetTraffic = useCallback((dbInbound: DBInbound) => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: t('pages.inbounds.resetConfirmTitle', { remark: dbInbound.remark }),
|
title: t('pages.inbounds.resetConfirmTitle', { remark: dbInbound.remark }),
|
||||||
content: t('pages.inbounds.resetConfirmContent'),
|
content: t('pages.inbounds.resetConfirmContent'),
|
||||||
|
|
@ -336,7 +342,7 @@ export default function InboundsPage() {
|
||||||
});
|
});
|
||||||
}, [modal, refresh, t]);
|
}, [modal, refresh, t]);
|
||||||
|
|
||||||
const confirmClone = useCallback((dbInbound: any) => {
|
const confirmClone = useCallback((dbInbound: DBInbound) => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
title: t('pages.inbounds.cloneConfirmTitle', { remark: dbInbound.remark }),
|
title: t('pages.inbounds.cloneConfirmTitle', { remark: dbInbound.remark }),
|
||||||
content: t('pages.inbounds.cloneConfirmContent'),
|
content: t('pages.inbounds.cloneConfirmContent'),
|
||||||
|
|
@ -350,7 +356,7 @@ export default function InboundsPage() {
|
||||||
raw.clients = [];
|
raw.clients = [];
|
||||||
clonedSettings = JSON.stringify(raw);
|
clonedSettings = JSON.stringify(raw);
|
||||||
} catch {
|
} catch {
|
||||||
clonedSettings = (Inbound as any).Settings.getSettings(baseInbound.protocol).toString();
|
clonedSettings = Inbound.Settings.getSettings(baseInbound.protocol).toString();
|
||||||
}
|
}
|
||||||
const data = {
|
const data = {
|
||||||
up: 0,
|
up: 0,
|
||||||
|
|
@ -393,7 +399,7 @@ export default function InboundsPage() {
|
||||||
}
|
}
|
||||||
}, [modal, importInbound, exportAllLinks, exportAllSubs, refresh, messageApi]);
|
}, [modal, importInbound, exportAllLinks, exportAllSubs, refresh, messageApi]);
|
||||||
|
|
||||||
const onRowAction = useCallback(async ({ key, dbInbound }: { key: RowAction; dbInbound: any }) => {
|
const onRowAction = useCallback(async ({ key, dbInbound }: { key: RowAction; dbInbound: DBInbound }) => {
|
||||||
// Actions that touch per-client secrets (uuid, password, flow, ...) need
|
// Actions that touch per-client secrets (uuid, password, flow, ...) need
|
||||||
// the full payload that the slim list view does not ship. Hydrate first
|
// the full payload that the slim list view does not ship. Hydrate first
|
||||||
// and then operate on the rehydrated record.
|
// and then operate on the rehydrated record.
|
||||||
|
|
@ -483,7 +489,7 @@ export default function InboundsPage() {
|
||||||
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<InboundList
|
<InboundList
|
||||||
dbInbounds={dbInbounds as any}
|
dbInbounds={dbInbounds}
|
||||||
clientCount={clientCount}
|
clientCount={clientCount}
|
||||||
onlineClients={onlineClients}
|
onlineClients={onlineClients}
|
||||||
lastOnlineMap={lastOnlineMap}
|
lastOnlineMap={lastOnlineMap}
|
||||||
|
|
@ -496,7 +502,7 @@ export default function InboundsPage() {
|
||||||
hasActiveNode={showNodeInfo}
|
hasActiveNode={showNodeInfo}
|
||||||
onAddInbound={onAddInbound}
|
onAddInbound={onAddInbound}
|
||||||
onGeneralAction={onGeneralAction}
|
onGeneralAction={onGeneralAction}
|
||||||
onRowAction={onRowAction}
|
onRowAction={({ key, dbInbound }) => onRowAction({ key, dbInbound: dbInbound as unknown as DBInbound })}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
@ -512,7 +518,7 @@ export default function InboundsPage() {
|
||||||
onSaved={refresh}
|
onSaved={refresh}
|
||||||
mode={formMode}
|
mode={formMode}
|
||||||
dbInbound={formDbInbound}
|
dbInbound={formDbInbound}
|
||||||
dbInbounds={dbInbounds as any[]}
|
dbInbounds={dbInbounds}
|
||||||
availableNodes={nodesList}
|
availableNodes={nodesList}
|
||||||
/>
|
/>
|
||||||
</LazyMount>
|
</LazyMount>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue