mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
refactor(frontend): port models/dbinbound to TypeScript
Phase 6 — final phase of the JS→TS migration. Frontend src/ no longer contains any *.js files. - DBInbound declares all fields explicitly (id, userId, up, down, total, …, nodeId, fallbackParent) with proper types - _expiryTime getter/setter typed against dayjs.Dayjs - coerceInboundJsonField takes unknown, returns any - Private cache fields (_cachedInbound, _clientStatsMap) declared - Consumers (InboundFormModal, InboundsPage, useInbounds): drop ".js" extension from @/models/dbinbound imports
This commit is contained in:
parent
2c8c30681b
commit
91ade9dfec
4 changed files with 46 additions and 25 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
import dayjs from 'dayjs';
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import dayjs, { type Dayjs } from 'dayjs';
|
||||||
import { ObjectUtil, NumberFormatter, SizeFormatter } from '@/utils';
|
import { ObjectUtil, NumberFormatter, SizeFormatter } from '@/utils';
|
||||||
import { Inbound, Protocols } from './inbound.js';
|
import { Inbound, Protocols } from './inbound';
|
||||||
|
|
||||||
export function coerceInboundJsonField(value) {
|
export function coerceInboundJsonField(value: unknown): any {
|
||||||
if (value == null) return {};
|
if (value == null) return {};
|
||||||
if (typeof value === 'object') return value;
|
if (typeof value === 'object') return value;
|
||||||
if (typeof value !== 'string') return {};
|
if (typeof value !== 'string') return {};
|
||||||
|
|
@ -10,14 +11,38 @@ export function coerceInboundJsonField(value) {
|
||||||
if (trimmed === '') return {};
|
if (trimmed === '') return {};
|
||||||
try {
|
try {
|
||||||
return JSON.parse(trimmed);
|
return JSON.parse(trimmed);
|
||||||
} catch (_e) {
|
} catch {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DBInbound {
|
export class DBInbound {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
up: number;
|
||||||
|
down: number;
|
||||||
|
total: number;
|
||||||
|
remark: string;
|
||||||
|
enable: boolean;
|
||||||
|
expiryTime: number;
|
||||||
|
trafficReset: string;
|
||||||
|
lastTrafficResetTime: number;
|
||||||
|
|
||||||
constructor(data) {
|
listen: string;
|
||||||
|
port: number;
|
||||||
|
protocol: string;
|
||||||
|
settings: any;
|
||||||
|
streamSettings: any;
|
||||||
|
tag: string;
|
||||||
|
sniffing: any;
|
||||||
|
clientStats: any;
|
||||||
|
nodeId: number | null;
|
||||||
|
fallbackParent: { masterId: number; path: string } | null;
|
||||||
|
|
||||||
|
private _cachedInbound: any = null;
|
||||||
|
private _clientStatsMap: Map<string, any> | null = null;
|
||||||
|
|
||||||
|
constructor(data?: any) {
|
||||||
this.id = 0;
|
this.id = 0;
|
||||||
this.userId = 0;
|
this.userId = 0;
|
||||||
this.up = 0;
|
this.up = 0;
|
||||||
|
|
@ -36,12 +61,8 @@ export class DBInbound {
|
||||||
this.streamSettings = "";
|
this.streamSettings = "";
|
||||||
this.tag = "";
|
this.tag = "";
|
||||||
this.sniffing = "";
|
this.sniffing = "";
|
||||||
this.clientStats = ""
|
this.clientStats = "";
|
||||||
// Optional FK to web/runtime registered Node. null/undefined =
|
|
||||||
// local panel; otherwise the inbound lives on the named node.
|
|
||||||
this.nodeId = null;
|
this.nodeId = null;
|
||||||
// Populated by the API when this inbound is a fallback child of
|
|
||||||
// a VLESS/Trojan TCP-TLS master. Shape: { masterId, path }.
|
|
||||||
this.fallbackParent = null;
|
this.fallbackParent = null;
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -49,11 +70,11 @@ export class DBInbound {
|
||||||
ObjectUtil.cloneProps(this, data);
|
ObjectUtil.cloneProps(this, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalGB() {
|
get totalGB(): number {
|
||||||
return NumberFormatter.toFixed(this.total / SizeFormatter.ONE_GB, 2);
|
return NumberFormatter.toFixed(this.total / SizeFormatter.ONE_GB, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
set totalGB(gb) {
|
set totalGB(gb: number) {
|
||||||
this.total = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
this.total = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +110,7 @@ export class DBInbound {
|
||||||
return this.protocol === Protocols.HYSTERIA;
|
return this.protocol === Protocols.HYSTERIA;
|
||||||
}
|
}
|
||||||
|
|
||||||
get address() {
|
get address(): string {
|
||||||
let address = location.hostname;
|
let address = location.hostname;
|
||||||
if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {
|
if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {
|
||||||
address = this.listen;
|
address = this.listen;
|
||||||
|
|
@ -97,14 +118,14 @@ export class DBInbound {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
get _expiryTime() {
|
get _expiryTime(): Dayjs | null {
|
||||||
if (this.expiryTime === 0) {
|
if (this.expiryTime === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return dayjs(this.expiryTime);
|
return dayjs(this.expiryTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
set _expiryTime(t) {
|
set _expiryTime(t: Dayjs | null | undefined) {
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
this.expiryTime = 0;
|
this.expiryTime = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -112,16 +133,16 @@ export class DBInbound {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isExpiry() {
|
get isExpiry(): boolean {
|
||||||
return this.expiryTime < new Date().getTime();
|
return this.expiryTime < new Date().getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateCache() {
|
invalidateCache(): void {
|
||||||
this._cachedInbound = null;
|
this._cachedInbound = null;
|
||||||
this._clientStatsMap = null;
|
this._clientStatsMap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
toInbound() {
|
toInbound(): any {
|
||||||
if (this._cachedInbound) {
|
if (this._cachedInbound) {
|
||||||
return this._cachedInbound;
|
return this._cachedInbound;
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +166,7 @@ export class DBInbound {
|
||||||
return this._cachedInbound;
|
return this._cachedInbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
getClientStats(email) {
|
getClientStats(email: string): any {
|
||||||
if (!this._clientStatsMap) {
|
if (!this._clientStatsMap) {
|
||||||
this._clientStatsMap = new Map();
|
this._clientStatsMap = new Map();
|
||||||
if (this.clientStats && Array.isArray(this.clientStats)) {
|
if (this.clientStats && Array.isArray(this.clientStats)) {
|
||||||
|
|
@ -157,7 +178,7 @@ export class DBInbound {
|
||||||
return this._clientStatsMap.get(email);
|
return this._clientStatsMap.get(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
isMultiUser() {
|
isMultiUser(): boolean {
|
||||||
switch (this.protocol) {
|
switch (this.protocol) {
|
||||||
case Protocols.VMESS:
|
case Protocols.VMESS:
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
|
|
@ -171,7 +192,7 @@ export class DBInbound {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLink() {
|
hasLink(): boolean {
|
||||||
switch (this.protocol) {
|
switch (this.protocol) {
|
||||||
case Protocols.VMESS:
|
case Protocols.VMESS:
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
|
|
@ -184,7 +205,7 @@ export class DBInbound {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genInboundLinks(remarkModel, hostOverride = '') {
|
genInboundLinks(remarkModel: string, hostOverride: string = ''): any[] {
|
||||||
const inbound = this.toInbound();
|
const inbound = this.toInbound();
|
||||||
return inbound.genInboundLinks(this.remark, remarkModel, hostOverride);
|
return inbound.genInboundLinks(this.remark, remarkModel, hostOverride);
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ import {
|
||||||
TCP_CONGESTION_OPTION,
|
TCP_CONGESTION_OPTION,
|
||||||
MODE_OPTION,
|
MODE_OPTION,
|
||||||
} from '@/models/inbound.js';
|
} from '@/models/inbound.js';
|
||||||
import { DBInbound } from '@/models/dbinbound.js';
|
import { DBInbound } from '@/models/dbinbound';
|
||||||
import FinalMaskForm from '@/components/FinalMaskForm';
|
import FinalMaskForm from '@/components/FinalMaskForm';
|
||||||
import DateTimePicker from '@/components/DateTimePicker';
|
import DateTimePicker from '@/components/DateTimePicker';
|
||||||
import JsonEditor from '@/components/JsonEditor';
|
import JsonEditor from '@/components/JsonEditor';
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import {
|
||||||
|
|
||||||
import { HttpUtil, SizeFormatter, RandomUtil } from '@/utils';
|
import { HttpUtil, SizeFormatter, RandomUtil } from '@/utils';
|
||||||
import { Inbound } from '@/models/inbound.js';
|
import { Inbound } from '@/models/inbound.js';
|
||||||
import { coerceInboundJsonField } from '@/models/dbinbound.js';
|
import { coerceInboundJsonField } 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';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { HttpUtil } from '@/utils';
|
import { HttpUtil } from '@/utils';
|
||||||
import { DBInbound } from '@/models/dbinbound.js';
|
import { DBInbound } from '@/models/dbinbound';
|
||||||
import { Protocols } from '@/models/inbound.js';
|
import { Protocols } from '@/models/inbound.js';
|
||||||
import { setDatepicker } from '@/hooks/useDatepicker';
|
import { setDatepicker } from '@/hooks/useDatepicker';
|
||||||
import { keys } from '@/api/queryKeys';
|
import { keys } from '@/api/queryKeys';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue